diff --git a/VERSION b/VERSION index be876f016..5b3a868b4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v2.0.3-2717-g52aacf37 +v2.0.3-2783-g1a8feab2 diff --git a/processing/post/perceptualUniformColorMap.py b/processing/post/perceptualUniformColorMap.py deleted file mode 100755 index 8e432536d..000000000 --- a/processing/post/perceptualUniformColorMap.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -from optparse import OptionParser - -import damask - - -scriptName = os.path.splitext(os.path.basename(__file__))[0] -scriptID = ' '.join([scriptName,damask.version]) - - -# -------------------------------------------------------------------- -# MAIN -# -------------------------------------------------------------------- -#Borland, D., & Taylor, R. M. (2007). Rainbow Color Map (Still) Considered Harmful. Computer Graphics and Applications, IEEE, 27(2), 14--17. -#Moreland, K. (2009). Diverging Color Maps for Scientific Visualization. In Proc. 5th Int. Symp. Visual Computing (pp. 92--103). -outtypes = ['paraview','gmsh','raw','GOM'] -extensions = ['.json','.msh','.txt','.legend'] -colormodels = ['RGB','HSL','XYZ','CIELAB','MSH'] - -parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [file[s]]', description = """ -Produces perceptually linear diverging and sequential colormaps in formats suitable for visualization software -or simply as a list of interpolated colors. - -""", version = scriptID) - -parser.add_option('-N','--steps', dest='N', type='int', nargs=1, metavar='int', - help='number of interpolation steps [%default]') -parser.add_option('-t','--trim', dest='trim', type='float', nargs=2, metavar='float float', - help='relative trim of colormap range [%default]') -parser.add_option('-l','--left', dest='left', type='float', nargs=3, metavar='float float float', - help='left color [%default]') -parser.add_option('-r','--right', dest='right', type='float', nargs=3, metavar='float float float', - help='right color [%default]') -parser.add_option('-c','--colormodel', dest='colormodel', metavar='string', - help='colormodel: '+', '.join(colormodels)+' [%default]') -parser.add_option('-p','--predefined', dest='predefined', metavar='string', - help='predefined colormap') -parser.add_option('-f','--format', dest='format', metavar='string', - help='output format: '+', '.join(outtypes)+' [%default]') -parser.set_defaults(colormodel = 'RGB') -parser.set_defaults(predefined = None) -parser.set_defaults(basename = None) -parser.set_defaults(format = 'paraview') -parser.set_defaults(N = 10) -parser.set_defaults(trim = (-1.0,1.0)) -parser.set_defaults(left = (1.0,1.0,1.0)) -parser.set_defaults(right = (0.0,0.0,0.0)) - -(options,filename) = parser.parse_args() - -if options.format not in outtypes: - parser.error('invalid format: "{}" (choices: {}).'.format(options.format,', '.join(outtypes))) - -if options.N < 2: - parser.error('too few steps (need at least 2).') - -if options.trim[0] < -1.0 or \ - options.trim[1] > 1.0 or \ - options.trim[0] >= options.trim[1]: - parser.error('invalid trim range (-1 +1).') - -name = options.format if filename == [] \ - else filename[0] -output = sys.stdout if filename == [] \ - else open(os.path.basename(filename[0])+extensions[outtypes.index(options.format)],'w') - -colorLeft = damask.Color(options.colormodel.upper(), list(options.left)) -colorRight = damask.Color(options.colormodel.upper(), list(options.right)) -colormap = damask.Colormap(colorLeft, colorRight, predefined=options.predefined) - -output.write(colormap.export(name,options.format,options.N,list(options.trim))) -output.close() diff --git a/python/damask/__init__.py b/python/damask/__init__.py index 6d65f7cd1..b2b94b6a5 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -10,7 +10,7 @@ with open(_Path(__file__).parent/_Path('VERSION')) as _f: from ._environment import Environment # noqa from ._table import Table # noqa from ._vtk import VTK # noqa -from ._colormaps import Colormap, Color # noqa +from ._colormap import Colormap # noqa from ._rotation import Rotation # noqa from ._lattice import Symmetry, Lattice# noqa from ._orientation import Orientation # noqa diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py new file mode 100644 index 000000000..9d57f234b --- /dev/null +++ b/python/damask/_colormap.py @@ -0,0 +1,612 @@ +import json +import functools + +import numpy as np +import matplotlib as mpl +import matplotlib.pyplot as plt +from matplotlib import cm + +import damask +from . import Table + +_eps = 216./24389. +_kappa = 24389./27. +_ref_white = np.array([.95047, 1.00000, 1.08883]) # Observer = 2, Illuminant = D65 + +# ToDo (if needed) +# - support alpha channel (paraview/ASCII/input) +# - support NaN color (paraview) + +class Colormap(mpl.colors.ListedColormap): + + def __add__(self,other): + """Concatenate colormaps.""" + return Colormap(np.vstack((self.colors,other.colors)), + f'{self.name}+{other.name}') + + def __iadd__(self,other): + """Concatenate colormaps.""" + return self.__add__(other) + + def __invert__(self): + """Return inverted colormap.""" + return self.reversed() + + @staticmethod + def from_range(low,high,name='DAMASK colormap',N=256,model='rgb'): + """ + Create a perceptually uniform colormap between given (inclusive) bounds. + + Colors are internally stored as R(ed) G(green) B(lue) values. + The colormap can be used in matplotlib/seaborn or exported to + file for external use. + + Parameters + ---------- + low : numpy.ndarray of shape (3) + Color definition for minimum value. + high : numpy.ndarray of shape (3) + Color definition for maximum value. + N : integer, optional + The number of color quantization levels. Defaults to 256. + name : str, optional + The name of the colormap. Defaults to `DAMASK colormap`. + model : {'rgb', 'hsv', 'hsl', 'xyz', 'lab', 'msh'} + Colormodel used for input color definitions. Defaults to `rgb`. + The available color models are: + - 'rgb': R(ed) G(green) B(lue). + - 'hsv': H(ue) S(aturation) V(alue). + - 'hsl': H(ue) S(aturation) L(uminance). + - 'xyz': CIE Xyz. + - 'lab': CIE Lab. + - 'msh': Msh (for perceptual uniform interpolation). + + """ + low_high = np.vstack((low,high)) + if model.lower() == 'rgb': + if np.any(low_high<0) or np.any(low_high>1): + raise ValueError(f'RGB color {low} | {high} are out of range.') + + low_,high_ = map(Colormap._rgb2msh,low_high) + + elif model.lower() == 'hsv': + if np.any(low_high<0) or np.any(low_high[:,1:3]>1) or np.any(low_high[:,0]>360): + raise ValueError(f'HSV color {low} | {high} are out of range.') + + low_,high_ = map(Colormap._hsv2msh,low_high) + + elif model.lower() == 'hsl': + if np.any(low_high<0) or np.any(low_high[:,1:3]>1) or np.any(low_high[:,0]>360): + raise ValueError(f'HSL color {low} | {high} are out of range.') + + low_,high_ = map(Colormap._hsl2msh,low_high) + + elif model.lower() == 'xyz': + + low_,high_ = map(Colormap._xyz2msh,low_high) + + elif model.lower() == 'lab': + if np.any(low_high[:,0]<0): + raise ValueError(f'CIE Lab color {low} | {high} are out of range.') + + low_,high_ = map(Colormap._lab2msh,low_high) + + elif model.lower() == 'msh': + low_,high_ = low_high[0],low_high[1] + + else: + raise ValueError(f'Invalid color model: {model}.') + + msh = map(functools.partial(Colormap._interpolate_msh,low=low_,high=high_),np.linspace(0,1,N)) + rgb = np.array(list(map(Colormap._msh2rgb,msh))) + + return Colormap(rgb,name=name) + + + @staticmethod + def from_predefined(name,N=256): + """ + Select from a set of predefined colormaps. + + Predefined colormaps include native matplotlib colormaps + and common DAMASK colormaps. + + Parameters + ---------- + name : str + The name of the colormap. + N : int, optional + The number of color quantization levels. Defaults to 256. + This parameter is not used for matplotlib colormaps + that are of type `ListedColormap`. + + """ + # matplotlib presets + for cat in Colormap._predefined_mpl: + for n in cat[1]: + if n == name: + colormap = cm.__dict__[name] + if isinstance(colormap,mpl.colors.LinearSegmentedColormap): + return Colormap(np.array(list(map(colormap,np.linspace(0,1,N)))),name=name) + else: + return Colormap(np.array(colormap.colors),name=name) + + # DAMASK presets + definition = Colormap._predefined_DAMASK[name] + return Colormap.from_range(definition['low'],definition['high'],name,N) + + + @staticmethod + def list_predefined(): + """ + List predefined colormaps by category. + + References + ---------- + .. [1] DAMASK colormap theory + https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + .. [2] DAMASK colormaps first use + https://doi.org/10.1016/j.ijplas.2012.09.012 + .. [3] Matplotlib colormaps overview + https://matplotlib.org/tutorials/colors/colormaps.html + + """ + print('DAMASK colormaps') + print(' '+', '.join(Colormap._predefined_DAMASK.keys())) + for cat in Colormap._predefined_mpl: + print(f'{cat[0]}') + print(' '+', '.join(cat[1])) + + + def show(self,aspect=10,vertical=False): + """Show colormap as matplotlib figure.""" + fig = plt.figure(figsize=(5/aspect,5) if vertical else (5,5/aspect)) + ax1 = fig.add_axes([0, 0, 1, 1]) + ax1.set_axis_off() + ax1.imshow(np.linspace(1 if vertical else 0, + 0 if vertical else 1, + self.N).reshape((-1,1) if vertical else (1,-1)), + aspect='auto', cmap=self, interpolation='nearest') + plt.show() + + + def reversed(self,name=None): + """ + Make a reversed instance of the colormap. + + Parameters + ---------- + name : str, optional + The name for the reversed colormap. + A name of None will be replaced by the name of the parent colormap + "_r". + + Returns + ------- + damask.Colormap + The reversed colormap. + + """ + rev = super(Colormap,self).reversed(name) + return Colormap(np.array(rev.colors),rev.name[:-4] if rev.name.endswith('_r_r') else rev.name) + + + def to_file(self,fname=None,format='ParaView'): + """ + Export colormap to file for use in external programs. + + Parameters + ---------- + fname : file, str, or pathlib.Path, optional. + Filename to store results. If not given, the filename will + consist of the name of the colormap and an extension that + depends on the file format. + format : {'ParaView', 'ASCII', 'GOM', 'gmsh'}, optional + File format, defaults to 'ParaView'. Available formats are: + - ParaView: JSON file, extension '.json'. + - ASCII: Plain text file, extension '.txt'. + - GOM: Aramis GOM (DIC), extension '.legend'. + - Gmsh: Gmsh FEM mesh-generator, extension '.msh'. + + """ + if fname is not None: + try: + f = open(fname,'w') + except TypeError: + f = fname + else: + f = None + + if format.lower() == 'paraview': + Colormap._export_paraview(self,f) + elif format.lower() == 'ascii': + Colormap._export_ASCII(self,f) + elif format.lower() == 'gom': + Colormap._export_GOM(self,f) + elif format.lower() == 'gmsh': + Colormap._export_gmsh(self,f) + else: + raise ValueError('Unknown output format: {format}.') + + @staticmethod + def _export_paraview(colormap,fhandle=None): + """Write colormap to JSON file for Paraview.""" + colors = [] + for i,c in enumerate(np.round(colormap.colors,6).tolist()): + colors+=[i]+c + + out = [{ + 'Creator':f'damask.Colormap v{damask.version}', + 'ColorSpace':'RGB', + 'Name':colormap.name, + 'DefaultMap':True, + 'RGBPoints':colors + }] + if fhandle is None: + with open(colormap.name.replace(' ','_')+'.json', 'w') as f: + json.dump(out, f,indent=4) + else: + json.dump(out,fhandle,indent=4) + + @staticmethod + def _export_ASCII(colormap,fhandle=None): + """Write colormap to ASCII table.""" + labels = {'RGBA':4} if colormap.colors.shape[1] == 4 else {'RGB': 3} + t = Table(colormap.colors,labels,f'Creator: damask.Colormap v{damask.version}') + + if fhandle is None: + with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: + t.to_ASCII(f,True) + else: + t.to_ASCII(fhandle,True) + + @staticmethod + def _export_GOM(colormap,fhandle=None): + """Write colormap to GOM Aramis compatible format.""" + # ToDo: test in GOM + GOM_str = f'1 1 {colormap.name.replace(" ","_")} 9 {colormap.name.replace(" ","_")} ' \ + + '0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' \ + + f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {len(colormap.colors)}' \ + + ' '.join([f' 0 {c[0]} {c[1]} {c[2]} 255 1' for c in reversed((colormap.colors*255).astype(int))]) \ + + '\n' + if fhandle is None: + with open(colormap.name.replace(' ','_')+'.legend', 'w') as f: + f.write(GOM_str) + else: + fhandle.write(GOM_str) + + + @staticmethod + def _export_gmsh(colormap,fhandle=None): + """Write colormap to Gmsh compatible format.""" + # ToDo: test in gmsh + gmsh_str = 'View.ColorTable = {\n' \ + +'\n'.join([f'{c[0]},{c[1]},{c[2]},' for c in colormap.colors[:,:3]*255]) \ + +'\n}\n' + if fhandle is None: + with open(colormap.name.replace(' ','_')+'.msh', 'w') as f: + f.write(gmsh_str) + else: + fhandle.write(gmsh_str) + + + @staticmethod + def _interpolate_msh(frac,low,high): + """ + Interpolate in Msh color space. + + This interpolation gives a perceptually uniform colormap. + + References + ---------- + https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + https://www.kennethmoreland.com/color-maps/diverging_map.py + + """ + def rad_diff(a,b): + return abs(a[2]-b[2]) + + def adjust_hue(msh_sat, msh_unsat): + """If saturation of one of the two colors is much less than the other, hue of the less.""" + if msh_sat[0] >= msh_unsat[0]: + return msh_sat[2] + else: + hSpin = msh_sat[1]/np.sin(msh_sat[1])*np.sqrt(msh_unsat[0]**2.0-msh_sat[0]**2)/msh_sat[0] + if msh_sat[2] < - np.pi/3.0: hSpin *= -1.0 + return msh_sat[2] + hSpin + + + lo = np.array(low) + hi = np.array(high) + + if (lo[1] > 0.05 and hi[1] > 0.05 and rad_diff(lo,hi) > np.pi/3.0): + M_mid = max(lo[0],hi[0],88.0) + if frac < 0.5: + hi = np.array([M_mid,0.0,0.0]) + frac *= 2.0 + else: + lo = np.array([M_mid,0.0,0.0]) + frac = 2.0*frac - 1.0 + if lo[1] < 0.05 and hi[1] > 0.05: + lo[2] = adjust_hue(hi,lo) + elif lo[1] > 0.05 and hi[1] < 0.05: + hi[2] = adjust_hue(lo,hi) + + return (1.0 - frac) * lo + frac * hi + + + _predefined_mpl= [('Perceptually Uniform Sequential', [ + 'viridis', 'plasma', 'inferno', 'magma', 'cividis']), + ('Sequential', [ + 'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds', + 'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu', + 'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']), + ('Sequential (2)', [ + 'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink', + 'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia', + 'hot', 'afmhot', 'gist_heat', 'copper']), + ('Diverging', [ + 'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu', + 'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']), + ('Cyclic', ['twilight', 'twilight_shifted', 'hsv']), + ('Qualitative', [ + 'Pastel1', 'Pastel2', 'Paired', 'Accent', + 'Dark2', 'Set1', 'Set2', 'Set3', + 'tab10', 'tab20', 'tab20b', 'tab20c']), + ('Miscellaneous', [ + 'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern', + 'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', + 'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar'])] + + _predefined_DAMASK = {'orientation': {'low': [0.933334,0.878432,0.878431], + 'high': [0.250980,0.007843,0.000000]}, + 'strain': {'low': [0.941177,0.941177,0.870588], + 'high': [0.266667,0.266667,0.000000]}, + 'stress': {'low': [0.878432,0.874511,0.949019], + 'high': [0.000002,0.000000,0.286275]}} + + @staticmethod + def _hsv2rgb(hsv): + """ + H(ue) S(aturation) V(alue) to R(red) G(reen) B(lue). + + References + ---------- + https://www.rapidtables.com/convert/color/hsv-to-rgb.html + + """ + sextant = np.clip(int(hsv[0]/60.),0,5) + c = hsv[1]*hsv[2] + x = c*(1.0 - abs((hsv[0]/60.)%2 - 1.)) + + return np.array([ + [c, x, 0], + [x, c, 0], + [0, c, x], + [0, x, c], + [x, 0, c], + [c, 0, x], + ])[sextant] + hsv[2] - c + + @staticmethod + def _rgb2hsv(rgb): + """ + R(ed) G(reen) B(lue) to H(ue) S(aturation) V(alue). + + References + ---------- + https://www.rapidtables.com/convert/color/rgb-to-hsv.html + + """ + C_max = rgb.max() + C_min = rgb.min() + Delta = C_max - C_min + + v = C_max + s = 0. if np.isclose(C_max,0.) else Delta/C_max + if np.isclose(Delta,0.): + h = 0. + elif rgb.argmax() == 0: + h = (rgb[1]-rgb[2])/Delta%6 + elif rgb.argmax() == 1: + h = (rgb[2]-rgb[0])/Delta + 2. + elif rgb.argmax() == 2: + h = (rgb[0]-rgb[1])/Delta + 4. + + h = np.clip(h,0.,6.) * 60. + + return np.array([h,s,v]) + + + @staticmethod + def _hsl2rgb(hsl): + """ + H(ue) S(aturation) L(uminance) to R(red) G(reen) B(lue). + + References + ---------- + https://www.rapidtables.com/convert/color/hsl-to-rgb.html + + """ + sextant = np.clip(int(hsl[0]/60.),0,5) + c = (1.0 - abs(2.0 * hsl[2] - 1.))*hsl[1] + x = c*(1.0 - abs((hsl[0]/60.)%2 - 1.)) + m = hsl[2] - 0.5*c + + return np.array([ + [c+m, x+m, m], + [x+m, c+m, m], + [m, c+m, x+m], + [m, x+m, c+m], + [x+m, m, c+m], + [c+m, m, x+m], + ])[sextant] + + @staticmethod + def _rgb2hsl(rgb): + """ + R(ed) G(reen) B(lue) to H(ue) S(aturation) L(uminance). + + References + ---------- + https://www.rapidtables.com/convert/color/rgb-to-hsl.html + + """ + C_max = rgb.max() + C_min = rgb.min() + Delta = C_max - C_min + + l = np.clip((C_max + C_min)*.5,0.,1.) # noqa + s = 0. if np.isclose(C_max,C_min) else Delta/(1.-np.abs(2*l-1.)) + if np.isclose(Delta,0.): + h = 0. + elif rgb.argmax() == 0: + h = (rgb[1]-rgb[2])/Delta%6 + elif rgb.argmax() == 1: + h = (rgb[2]-rgb[0])/Delta + 2. + elif rgb.argmax() == 2: + h = (rgb[0]-rgb[1])/Delta + 4. + + h = np.clip(h,0.,6.) * 60. + + return np.array([h,s,l]) + + + @staticmethod + def _xyz2rgb(xyz): + """ + CIE Xyz to R(ed) G(reen) B(lue). + + References + ---------- + http://www.ryanjuckett.com/programming/rgb-color-space-conversion + + """ + rgb_lin = np.dot(np.array([ + [ 3.240969942,-1.537383178,-0.498610760], + [-0.969243636, 1.875967502, 0.041555057], + [ 0.055630080,-0.203976959, 1.056971514] + ]),xyz) + with np.errstate(invalid='ignore'): + rgb = np.where(rgb_lin>0.0031308,rgb_lin**(1.0/2.4)*1.0555-0.0555,rgb_lin*12.92) + + return np.clip(rgb,0.,1.) + + @staticmethod + def _rgb2xyz(rgb): + """ + R(ed) G(reen) B(lue) to CIE Xyz. + + References + ---------- + http://www.ryanjuckett.com/programming/rgb-color-space-conversion + + """ + rgb_lin = np.where(rgb>0.04045,((rgb+0.0555)/1.0555)**2.4,rgb/12.92) + return np.dot(np.array([ + [0.412390799,0.357584339,0.180480788], + [0.212639006,0.715168679,0.072192315], + [0.019330819,0.119194780,0.950532152] + ]),rgb_lin) + + + @staticmethod + def _lab2xyz(lab,ref_white=None): + """ + CIE Lab to CIE Xyz. + + References + ---------- + http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + + """ + f_x = (lab[0]+16.)/116. + lab[1]/500. + f_z = (lab[0]+16.)/116. - lab[2]/200. + + return np.array([ + f_x**3. if f_x**3. > _eps else (116.*f_x-16.)/_kappa, + ((lab[0]+16.)/116.)**3 if lab[0]>_kappa*_eps else lab[0]/_kappa, + f_z**3. if f_z**3. > _eps else (116.*f_z-16.)/_kappa + ])*(ref_white if ref_white is not None else _ref_white) + + @staticmethod + def _xyz2lab(xyz,ref_white=None): + """ + CIE Xyz to CIE Lab. + + References + ---------- + http://www.brucelindbloom.com/index.html?Eqn_Lab_to_XYZ.html + + """ + ref_white = ref_white if ref_white is not None else _ref_white + f = np.where(xyz/ref_white > _eps,(xyz/ref_white)**(1./3.),(_kappa*xyz/ref_white+16.)/116.) + + return np.array([ + 116.0 * f[1] - 16.0, + 500.0 * (f[0] - f[1]), + 200.0 * (f[1] - f[2]) + ]) + + + @staticmethod + def _lab2msh(lab): + """ + CIE Lab to Msh. + + References + ---------- + https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + https://www.kennethmoreland.com/color-maps/diverging_map.py + + """ + M = np.linalg.norm(lab) + return np.array([ + M, + np.arccos(lab[0]/M) if M>1e-8 else 0., + np.arctan2(lab[2],lab[1]) if M>1e-8 else 0., + ]) + + @staticmethod + def _msh2lab(msh): + """ + Msh to CIE Lab. + + References + ---------- + https://www.kennethmoreland.com/color-maps/ColorMapsExpanded.pdf + https://www.kennethmoreland.com/color-maps/diverging_map.py + + """ + return np.array([ + msh[0] * np.cos(msh[1]), + msh[0] * np.sin(msh[1]) * np.cos(msh[2]), + msh[0] * np.sin(msh[1]) * np.sin(msh[2]) + ]) + + @staticmethod + def _lab2rgb(lab): + return Colormap._xyz2rgb(Colormap._lab2xyz(lab)) + + @staticmethod + def _rgb2lab(rgb): + return Colormap._xyz2lab(Colormap._rgb2xyz(rgb)) + + @staticmethod + def _msh2rgb(msh): + return Colormap._lab2rgb(Colormap._msh2lab(msh)) + + @staticmethod + def _rgb2msh(rgb): + return Colormap._lab2msh(Colormap._rgb2lab(rgb)) + + @staticmethod + def _hsv2msh(hsv): + return Colormap._rgb2msh(Colormap._hsv2rgb(hsv)) + + @staticmethod + def _hsl2msh(hsl): + return Colormap._rgb2msh(Colormap._hsl2rgb(hsl)) + + @staticmethod + def _xyz2msh(xyz): + return Colormap._lab2msh(Colormap._xyz2lab(xyz)) diff --git a/python/damask/_colormaps.py b/python/damask/_colormaps.py deleted file mode 100644 index 6c6f82604..000000000 --- a/python/damask/_colormaps.py +++ /dev/null @@ -1,541 +0,0 @@ -import numpy as np -from . import util - -class Color: - """Color representation in and conversion between different color-spaces.""" - - __slots__ = [ - 'model', - 'color', - '__dict__', - ] - - - def __init__(self, - model = 'RGB', - color = np.zeros(3)): - """ - Create a Color object. - - Parameters - ---------- - model : string - color model - color : numpy.ndarray - vector representing the color according to the selected model - - """ - self.__transforms__ = \ - {'HSV': {'index': 0, 'next': self._HSV2HSL}, - 'HSL': {'index': 1, 'next': self._HSL2RGB, 'prev': self._HSL2HSV}, - 'RGB': {'index': 2, 'next': self._RGB2XYZ, 'prev': self._RGB2HSL}, - 'XYZ': {'index': 3, 'next': self._XYZ2CIELAB, 'prev': self._XYZ2RGB}, - 'CIELAB': {'index': 4, 'next': self._CIELAB2MSH, 'prev': self._CIELAB2XYZ}, - 'MSH': {'index': 5, 'prev': self._MSH2CIELAB}, - } - - model = model.upper() - if model not in list(self.__transforms__.keys()): model = 'RGB' - if model == 'RGB' and max(color) > 1.0: # are we RGB255 ? - for i in range(3): - color[i] /= 255.0 # rescale to RGB - - if model == 'HSL': # are we HSL ? - if abs(color[0]) > 1.0: color[0] /= 360.0 # with angular hue? - while color[0] >= 1.0: color[0] -= 1.0 # rewind to proper range - while color[0] < 0.0: color[0] += 1.0 # rewind to proper range - - self.model = model - self.color = np.array(color,'d') - - - def __repr__(self): - """Color model and values.""" - return 'Model: %s Color: %s'%(self.model,str(self.color)) - - - def __str__(self): - """Color model and values.""" - return self.__repr__() - - - def convert_to(self,toModel = 'RGB'): - """ - Change the color model permanently. - - Parameters - ---------- - toModel : string - color model - - """ - toModel = toModel.upper() - if toModel not in list(self.__transforms__.keys()): return - - sourcePos = self.__transforms__[self.model]['index'] - targetPos = self.__transforms__[toModel]['index'] - - while sourcePos < targetPos: - self.__transforms__[self.model]['next']() - sourcePos += 1 - - while sourcePos > targetPos: - self.__transforms__[self.model]['prev']() - sourcePos -= 1 - return self - - - def express_as(self,asModel = 'RGB'): - """ - Return the color in a different model. - - Parameters - ---------- - asModel : string - color model - - """ - return self.__class__(self.model,self.color).convert_to(asModel) - - - def _HSV2HSL(self): - """ - Convert H(ue) S(aturation) V(alue or brightness) to H(ue) S(aturation) L(uminance). - - All values are in the range [0,1] - http://codeitdown.com/hsl-hsb-hsv-color - """ - if self.model != 'HSV': return - - converted = Color('HSL',np.array([ - self.color[0], - 1. if self.color[2] == 0.0 or (self.color[1] == 0.0 and self.color[2] == 1.0) \ - else self.color[1]*self.color[2]/(1.-abs(self.color[2]*(2.-self.color[1])-1.)), - 0.5*self.color[2]*(2.-self.color[1]), - ])) - - self.model = converted.model - self.color = converted.color - - - def _HSL2HSV(self): - """ - Convert H(ue) S(aturation) L(uminance) to H(ue) S(aturation) V(alue or brightness). - - All values are in the range [0,1] - http://codeitdown.com/hsl-hsb-hsv-color - """ - if self.model != 'HSL': return - - h = self.color[0] - b = self.color[2]+0.5*(self.color[1]*(1.-abs(2*self.color[2]-1))) - s = 1.0 if b == 0.0 else 2.*(b-self.color[2])/b - - converted = Color('HSV',np.array([h,s,b])) - - self.model = converted.model - self.color = converted.color - - - def _HSL2RGB(self): - """ - Convert H(ue) S(aturation) L(uminance) to R(red) G(reen) B(lue). - - All values are in the range [0,1] - from http://en.wikipedia.org/wiki/HSL_and_HSV - """ - if self.model != 'HSL': return - - sextant = self.color[0]*6.0 - c = (1.0 - abs(2.0 * self.color[2] - 1.0))*self.color[1] - x = c*(1.0 - abs(sextant%2 - 1.0)) - m = self.color[2] - 0.5*c - - converted = Color('RGB',np.array([ - [c+m, x+m, m], - [x+m, c+m, m], - [m, c+m, x+m], - [m, x+m, c+m], - [x+m, m, c+m], - [c+m, m, x+m], - ][int(sextant)])) - self.model = converted.model - self.color = converted.color - - - def _RGB2HSL(self): - """ - Convert R(ed) G(reen) B(lue) to H(ue) S(aturation) L(uminance). - - All values are in the range [0,1] - from http://130.113.54.154/~monger/hsl-rgb.html - """ - if self.model != 'RGB': return - - HSL = np.zeros(3) - maxcolor = self.color.max() - mincolor = self.color.min() - HSL[2] = (maxcolor + mincolor)/2.0 - if(mincolor == maxcolor): - HSL[0] = 0.0 - HSL[1] = 0.0 - else: - if (HSL[2]<0.5): - HSL[1] = (maxcolor - mincolor)/(maxcolor + mincolor) - else: - HSL[1] = (maxcolor - mincolor)/(2.0 - maxcolor - mincolor) - if (maxcolor == self.color[0]): - HSL[0] = 0.0 + (self.color[1] - self.color[2])/(maxcolor - mincolor) - elif (maxcolor == self.color[1]): - HSL[0] = 2.0 + (self.color[2] - self.color[0])/(maxcolor - mincolor) - elif (maxcolor == self.color[2]): - HSL[0] = 4.0 + (self.color[0] - self.color[1])/(maxcolor - mincolor) - HSL[0] = HSL[0]*60.0 # scaling to 360 might be dangerous for small values - if (HSL[0] < 0.0): - HSL[0] = HSL[0] + 360.0 - HSL[1:] = np.clip(HSL[1:],0.0,1.0) - - converted = Color('HSL', HSL) - self.model = converted.model - self.color = converted.color - - - def _RGB2XYZ(self): - """ - Convert R(ed) G(reen) B(lue) to CIE XYZ. - - All values are in the range [0,1] - from http://www.cs.rit.edu/~ncs/color/t_convert.html - """ - if self.model != 'RGB': return - - XYZ = np.zeros(3) - RGB_lin = np.zeros(3) - convert = np.array([[0.412453,0.357580,0.180423], - [0.212671,0.715160,0.072169], - [0.019334,0.119193,0.950227]]) - - for i in range(3): - if (self.color[i] > 0.04045): RGB_lin[i] = ((self.color[i]+0.0555)/1.0555)**2.4 - else: RGB_lin[i] = self.color[i] /12.92 - XYZ = np.dot(convert,RGB_lin) - XYZ = np.clip(XYZ,0.0,None) - - converted = Color('XYZ', XYZ) - self.model = converted.model - self.color = converted.color - - - def _XYZ2RGB(self): - """ - Convert CIE XYZ to R(ed) G(reen) B(lue). - - All values are in the range [0,1] - from http://www.cs.rit.edu/~ncs/color/t_convert.html - """ - if self.model != 'XYZ': return - - convert = np.array([[ 3.240479,-1.537150,-0.498535], - [-0.969256, 1.875992, 0.041556], - [ 0.055648,-0.204043, 1.057311]]) - RGB_lin = np.dot(convert,self.color) - RGB = np.zeros(3) - - for i in range(3): - if (RGB_lin[i] > 0.0031308): RGB[i] = ((RGB_lin[i])**(1.0/2.4))*1.0555-0.0555 - else: RGB[i] = RGB_lin[i] *12.92 - - RGB = np.clip(RGB,0.0,1.0) - - maxVal = max(RGB) # clipping colors according to the display gamut - if (maxVal > 1.0): RGB /= maxVal - - converted = Color('RGB', RGB) - self.model = converted.model - self.color = converted.color - - - def _CIELAB2XYZ(self): - """ - Convert CIE Lab to CIE XYZ. - - All values are in the range [0,1] - from http://www.easyrgb.com/index.php?X=MATH&H=07#text7 - """ - if self.model != 'CIELAB': return - - ref_white = np.array([.95047, 1.00000, 1.08883]) # Observer = 2, Illuminant = D65 - XYZ = np.zeros(3) - - XYZ[1] = (self.color[0] + 16.0 ) / 116.0 - XYZ[0] = XYZ[1] + self.color[1]/ 500.0 - XYZ[2] = XYZ[1] - self.color[2]/ 200.0 - - for i in range(len(XYZ)): - if (XYZ[i] > 6./29. ): XYZ[i] = XYZ[i]**3. - else: XYZ[i] = 108./841. * (XYZ[i] - 4./29.) - - converted = Color('XYZ', XYZ*ref_white) - self.model = converted.model - self.color = converted.color - - - def _XYZ2CIELAB(self): - """ - Convert CIE XYZ to CIE Lab. - - All values are in the range [0,1] - from http://en.wikipedia.org/wiki/Lab_color_space, - http://www.cs.rit.edu/~ncs/color/t_convert.html - """ - if self.model != 'XYZ': return - - ref_white = np.array([.95047, 1.00000, 1.08883]) # Observer = 2, Illuminant = D65 - XYZ = self.color/ref_white - - for i in range(len(XYZ)): - if (XYZ[i] > 216./24389 ): XYZ[i] = XYZ[i]**(1.0/3.0) - else: XYZ[i] = (841./108. * XYZ[i]) + 16.0/116.0 - - converted = Color('CIELAB', np.array([ 116.0 * XYZ[1] - 16.0, - 500.0 * (XYZ[0] - XYZ[1]), - 200.0 * (XYZ[1] - XYZ[2]) ])) - self.model = converted.model - self.color = converted.color - - - def _CIELAB2MSH(self): - """ - Convert CIE Lab to Msh colorspace. - - from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls - """ - if self.model != 'CIELAB': return - - Msh = np.zeros(3) - Msh[0] = np.sqrt(np.dot(self.color,self.color)) - if (Msh[0] > 0.001): - Msh[1] = np.arccos(self.color[0]/Msh[0]) - if (self.color[1] != 0.0): - Msh[2] = np.arctan2(self.color[2],self.color[1]) - - converted = Color('MSH', Msh) - self.model = converted.model - self.color = converted.color - - - def _MSH2CIELAB(self): - """ - Convert Msh colorspace to CIE Lab. - - with s,h in radians - from http://www.cs.unm.edu/~kmorel/documents/ColorMaps/DivergingColorMapWorkshop.xls - """ - if self.model != 'MSH': return - - Lab = np.zeros(3) - Lab[0] = self.color[0] * np.cos(self.color[1]) - Lab[1] = self.color[0] * np.sin(self.color[1]) * np.cos(self.color[2]) - Lab[2] = self.color[0] * np.sin(self.color[1]) * np.sin(self.color[2]) - - converted = Color('CIELAB', Lab) - self.model = converted.model - self.color = converted.color - - -class Colormap: - """Perceptually uniform diverging or sequential colormap.""" - - __slots__ = [ - 'left', - 'right', - 'interpolate', - ] - __predefined__ = { - 'gray': {'left': Color('HSL',[0,1,1]), - 'right': Color('HSL',[0,0,0.15]), - 'interpolate': 'perceptualuniform'}, - 'grey': {'left': Color('HSL',[0,1,1]), - 'right': Color('HSL',[0,0,0.15]), - 'interpolate': 'perceptualuniform'}, - 'red': {'left': Color('HSL',[0,1,0.14]), - 'right': Color('HSL',[0,0.35,0.91]), - 'interpolate': 'perceptualuniform'}, - 'green': {'left': Color('HSL',[0.33333,1,0.14]), - 'right': Color('HSL',[0.33333,0.35,0.91]), - 'interpolate': 'perceptualuniform'}, - 'blue': {'left': Color('HSL',[0.66,1,0.14]), - 'right': Color('HSL',[0.66,0.35,0.91]), - 'interpolate': 'perceptualuniform'}, - 'seaweed': {'left': Color('HSL',[0.78,1.0,0.1]), - 'right': Color('HSL',[0.40000,0.1,0.9]), - 'interpolate': 'perceptualuniform'}, - 'bluebrown': {'left': Color('HSL',[0.65,0.53,0.49]), - 'right': Color('HSL',[0.11,0.75,0.38]), - 'interpolate': 'perceptualuniform'}, - 'redgreen': {'left': Color('HSL',[0.97,0.96,0.36]), - 'right': Color('HSL',[0.33333,1.0,0.14]), - 'interpolate': 'perceptualuniform'}, - 'bluered': {'left': Color('HSL',[0.65,0.53,0.49]), - 'right': Color('HSL',[0.97,0.96,0.36]), - 'interpolate': 'perceptualuniform'}, - 'blueredrainbow':{'left': Color('HSL',[2.0/3.0,1,0.5]), - 'right': Color('HSL',[0,1,0.5]), - 'interpolate': 'linear' }, - 'orientation': {'left': Color('RGB',[0.933334,0.878432,0.878431]), - 'right': Color('RGB',[0.250980,0.007843,0.000000]), - 'interpolate': 'perceptualuniform'}, - 'strain': {'left': Color('RGB',[0.941177,0.941177,0.870588]), - 'right': Color('RGB',[0.266667,0.266667,0.000000]), - 'interpolate': 'perceptualuniform'}, - 'stress': {'left': Color('RGB',[0.878432,0.874511,0.949019]), - 'right': Color('RGB',[0.000002,0.000000,0.286275]), - 'interpolate': 'perceptualuniform'}, - } - - - def __init__(self, - left = Color('RGB',[1,1,1]), - right = Color('RGB',[0,0,0]), - interpolate = 'perceptualuniform', - 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: - left = self.__predefined__[predefined.lower()]['left'] - right= self.__predefined__[predefined.lower()]['right'] - interpolate = self.__predefined__[predefined.lower()]['interpolate'] - - if left.__class__.__name__ != 'Color': - left = Color() - if right.__class__.__name__ != 'Color': - right = Color() - - self.left = left - self.right = right - self.interpolate = interpolate - - - def __repr__(self): - """Left and right value of colormap.""" - return 'Left: %s Right: %s'%(self.left,self.right) - - - def invert(self): - """Switch left/minimum with right/maximum.""" - (self.left, self.right) = (self.right, self.left) - 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 interpolate_Msh(lo, hi, frac): - - def rad_diff(a,b): - return abs(a[2]-b[2]) - - def adjust_hue(Msh_sat, Msh_unsat): - """If saturation of one of the two colors is too less than the other, hue of the less.""" - if Msh_sat[0] >= Msh_unsat[0]: - return Msh_sat[2] - else: - hSpin = Msh_sat[1]/np.sin(Msh_sat[1])*np.sqrt(Msh_unsat[0]**2.0-Msh_sat[0]**2)/Msh_sat[0] - if Msh_sat[2] < - np.pi/3.0: hSpin *= -1.0 - return Msh_sat[2] + hSpin - - Msh1 = np.array(lo[:]) - Msh2 = np.array(hi[:]) - - if (Msh1[1] > 0.05 and Msh2[1] > 0.05 and rad_diff(Msh1,Msh2) > np.pi/3.0): - M_mid = max(Msh1[0],Msh2[0],88.0) - if frac < 0.5: - Msh2 = np.array([M_mid,0.0,0.0]) - frac *= 2.0 - else: - Msh1 = np.array([M_mid,0.0,0.0]) - frac = 2.0*frac - 1.0 - if Msh1[1] < 0.05 and Msh2[1] > 0.05: Msh1[2] = adjust_hue(Msh2,Msh1) - elif Msh1[1] > 0.05 and Msh2[1] < 0.05: Msh2[2] = adjust_hue(Msh1,Msh2) - Msh = (1.0 - frac) * Msh1 + frac * Msh2 - - return Color('MSH',Msh) - - def interpolate_linear(lo, hi, frac): - """Linear interpolation between lo and hi color at given fraction; output in model of lo color.""" - interpolation = (1.0 - frac) * np.array(lo.color[:]) \ - + frac * np.array(hi.express_as(lo.model).color[:]) - - return Color(lo.model,interpolation) - - if self.interpolate == 'perceptualuniform': - return interpolate_Msh(self.left.express_as('MSH').color, - self.right.express_as('MSH').color,fraction) - elif self.interpolate == 'linear': - return interpolate_linear(self.left,self.right,fraction) - else: - raise NameError('unknown color interpolation method') - - - def export(self,name = 'uniformPerceptualColorMap',\ - format = 'paraview',\ - steps = 2,\ - crop = [-1.0,1.0], - model = 'RGB'): - """ - [RGB] colormap for use in paraview or gmsh, or as raw string, or array. - - Arguments: name, format, steps, crop. - Format is one of (paraview, gmsh, gom, raw, list). - Crop selects a (sub)range in [-1.0,1.0]. - Generates sequential map if one limiting color is either white or black, - diverging map otherwise. - """ - format = format.lower() # consistent comparison basis - 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]).express_as(model).color for i in range(steps)] - if format == 'paraview': - colormap = [f'[\n {{\n "ColorSpace": "RGB", "Name": "{name}", "DefaultMap": true,\n "RGBPoints" : ['] \ - + [f' {i:4d},{color[0]:8.6f},{color[1]:8.6f},{color[2]:8.6f}{"," if i+1 otherOrder) - (myOrder < otherOrder) - def symmetryOperations(self,members=[]): - """List (or single element) of symmetry operations as rotations.""" - if self.lattice == 'cubic': - symQuats = [ + + @property + def symmetry_operations(self): + """Symmetry operations as quaternions.""" + if self.system == 'cubic': + sym_quats = [ [ 1.0, 0.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0, 0.0 ], [ 0.0, 0.0, 1.0, 0.0 ], @@ -110,8 +110,8 @@ class Symmetry: [-0.5*np.sqrt(2), 0.5*np.sqrt(2), 0.0, 0.0 ], [-0.5*np.sqrt(2),-0.5*np.sqrt(2), 0.0, 0.0 ], ] - elif self.lattice == 'hexagonal': - symQuats = [ + elif self.system == 'hexagonal': + sym_quats = [ [ 1.0, 0.0, 0.0, 0.0 ], [-0.5*np.sqrt(3), 0.0, 0.0, -0.5 ], [ 0.5, 0.0, 0.0, 0.5*np.sqrt(3) ], @@ -125,8 +125,8 @@ class Symmetry: [ 0.0, -0.5, -0.5*np.sqrt(3), 0.0 ], [ 0.0, 0.5*np.sqrt(3), 0.5, 0.0 ], ] - elif self.lattice == 'tetragonal': - symQuats = [ + elif self.system == 'tetragonal': + sym_quats = [ [ 1.0, 0.0, 0.0, 0.0 ], [ 0.0, 1.0, 0.0, 0.0 ], [ 0.0, 0.0, 1.0, 0.0 ], @@ -136,64 +136,54 @@ class Symmetry: [ 0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], [-0.5*np.sqrt(2), 0.0, 0.0, 0.5*np.sqrt(2) ], ] - elif self.lattice == 'orthorhombic': - symQuats = [ + elif self.system == 'orthorhombic': + sym_quats = [ [ 1.0,0.0,0.0,0.0 ], [ 0.0,1.0,0.0,0.0 ], [ 0.0,0.0,1.0,0.0 ], [ 0.0,0.0,0.0,1.0 ], ] else: - symQuats = [ + sym_quats = [ [ 1.0,0.0,0.0,0.0 ], ] - - symOps = list(map(Rotation, - np.array(symQuats)[np.atleast_1d(members) if members != [] else range(len(symQuats))])) - try: - iter(members) # asking for (even empty) list of members? - except TypeError: - return symOps[0] # no, return rotation object - else: - return symOps # yes, return list of rotations + return np.array(sym_quats) - def inFZ(self,rodrigues): + def in_FZ(self,rho): """ - Check whether given Rodrigues-Frank vector falls into fundamental zone of own symmetry. + Check whether given Rodrigues-Frank vector falls into fundamental zone. Fundamental zone in Rodrigues space is point symmetric around origin. """ - if (len(rodrigues) != 3): - raise ValueError('Input is not a Rodrigues-Frank vector.\n') + if(rho.shape[-1] != 3): + raise ValueError('Input is not a Rodrigues-Frank vector field.') - if np.any(rodrigues == np.inf): return False + rho_abs = np.abs(rho) - Rabs = abs(rodrigues) - - if self.lattice == 'cubic': - return np.sqrt(2.0)-1.0 >= Rabs[0] \ - and np.sqrt(2.0)-1.0 >= Rabs[1] \ - and np.sqrt(2.0)-1.0 >= Rabs[2] \ - and 1.0 >= Rabs[0] + Rabs[1] + Rabs[2] - elif self.lattice == 'hexagonal': - return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] and 1.0 >= Rabs[2] \ - and 2.0 >= np.sqrt(3)*Rabs[0] + Rabs[1] \ - and 2.0 >= np.sqrt(3)*Rabs[1] + Rabs[0] \ - and 2.0 >= np.sqrt(3) + Rabs[2] - elif self.lattice == 'tetragonal': - return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] \ - and np.sqrt(2.0) >= Rabs[0] + Rabs[1] \ - and np.sqrt(2.0) >= Rabs[2] + 1.0 - elif self.lattice == 'orthorhombic': - return 1.0 >= Rabs[0] and 1.0 >= Rabs[1] and 1.0 >= Rabs[2] - else: - return True + with np.errstate(invalid='ignore'): + # using '*'/prod for 'and' + if self.system == 'cubic': + return np.where(np.prod(np.sqrt(2)-1. >= rho_abs,axis=-1) * \ + (1. >= np.sum(rho_abs,axis=-1)),True,False) + elif self.system == 'hexagonal': + return np.where(np.prod(1. >= rho_abs,axis=-1) * \ + (2. >= np.sqrt(3)*rho_abs[...,0] + rho_abs[...,1]) * \ + (2. >= np.sqrt(3)*rho_abs[...,1] + rho_abs[...,0]) * \ + (2. >= np.sqrt(3) + rho_abs[...,2]),True,False) + elif self.system == 'tetragonal': + return np.where(np.prod(1. >= rho_abs[...,:2],axis=-1) * \ + (np.sqrt(2) >= rho_abs[...,0] + rho_abs[...,1]) * \ + (np.sqrt(2) >= rho_abs[...,2] + 1.),True,False) + elif self.system == 'orthorhombic': + return np.where(np.prod(1. >= rho_abs,axis=-1),True,False) + else: + return np.where(np.all(np.isfinite(rho_abs),axis=-1),True,False) - def inDisorientationSST(self,rodrigues): + def in_disorientation_SST(self,rho): """ - Check whether given Rodrigues-Frank vector (of misorientation) falls into standard stereographic triangle of own symmetry. + Check whether given Rodrigues-Frank vector (of misorientation) falls into standard stereographic triangle. References ---------- @@ -201,27 +191,33 @@ class Symmetry: https://doi.org/10.1107/S0108767391006864 """ - if (len(rodrigues) != 3): - raise ValueError('Input is not a Rodrigues-Frank vector.\n') - R = rodrigues + if(rho.shape[-1] != 3): + raise ValueError('Input is not a Rodrigues-Frank vector field.') - epsilon = 0.0 - if self.lattice == 'cubic': - return R[0] >= R[1]+epsilon and R[1] >= R[2]+epsilon and R[2] >= epsilon - elif self.lattice == 'hexagonal': - return R[0] >= np.sqrt(3)*(R[1]-epsilon) and R[1] >= epsilon and R[2] >= epsilon - elif self.lattice == 'tetragonal': - return R[0] >= R[1]-epsilon and R[1] >= epsilon and R[2] >= epsilon - elif self.lattice == 'orthorhombic': - return R[0] >= epsilon and R[1] >= epsilon and R[2] >= epsilon - else: - return True + with np.errstate(invalid='ignore'): + # using '*' for 'and' + if self.system == 'cubic': + return np.where((rho[...,0] >= rho[...,1]) * \ + (rho[...,1] >= rho[...,2]) * \ + (rho[...,2] >= 0),True,False) + elif self.system == 'hexagonal': + return np.where((rho[...,0] >= rho[...,1]*np.sqrt(3)) * \ + (rho[...,1] >= 0) * \ + (rho[...,2] >= 0),True,False) + elif self.system == 'tetragonal': + return np.where((rho[...,0] >= rho[...,1]) * \ + (rho[...,1] >= 0) * \ + (rho[...,2] >= 0),True,False) + elif self.system == 'orthorhombic': + return np.where((rho[...,0] >= 0) * \ + (rho[...,1] >= 0) * \ + (rho[...,2] >= 0),True,False) + else: + return np.ones_like(rho[...,0],dtype=bool) - def inSST(self, - vector, - proper = False, - color = False): + #ToDo: IPF color in separate function + def in_SST(self,vector,proper=False,color=False): """ Check whether given vector falls into standard stereographic triangle of own symmetry. @@ -244,7 +240,10 @@ class Symmetry: ... } """ - if self.lattice == 'cubic': + if(vector.shape[-1] != 3): + raise ValueError('Input is not a 3D vector field.') + + if self.system == 'cubic': basis = {'improper':np.array([ [-1. , 0. , 1. ], [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], [ 0. , np.sqrt(3.) , 0. ] ]), @@ -252,7 +251,7 @@ class Symmetry: [-np.sqrt(2.) , np.sqrt(2.) , 0. ], [ np.sqrt(3.) , 0. , 0. ] ]), } - elif self.lattice == 'hexagonal': + elif self.system == 'hexagonal': basis = {'improper':np.array([ [ 0. , 0. , 1. ], [ 1. , -np.sqrt(3.) , 0. ], [ 0. , 2. , 0. ] ]), @@ -260,7 +259,7 @@ class Symmetry: [-1. , np.sqrt(3.) , 0. ], [ np.sqrt(3.) , -1. , 0. ] ]), } - elif self.lattice == 'tetragonal': + elif self.system == 'tetragonal': basis = {'improper':np.array([ [ 0. , 0. , 1. ], [ 1. , -1. , 0. ], [ 0. , np.sqrt(2.) , 0. ] ]), @@ -268,7 +267,7 @@ class Symmetry: [-1. , 1. , 0. ], [ np.sqrt(2.) , 0. , 0. ] ]), } - elif self.lattice == 'orthorhombic': + elif self.system == 'orthorhombic': basis = {'improper':np.array([ [ 0., 0., 1.], [ 1., 0., 0.], [ 0., 1., 0.] ]), @@ -278,43 +277,41 @@ class Symmetry: } else: # direct exit for unspecified symmetry if color: - return (True,np.zeros(3,'d')) + return (np.ones_like(vector[...,0],bool),np.zeros_like(vector)) else: - return True + return np.ones_like(vector[...,0],bool) - v = np.array(vector,dtype=float) - if proper: # check both improper ... - theComponents = np.around(np.dot(basis['improper'],v),12) - inSST = np.all(theComponents >= 0.0) - if not inSST: # ... and proper SST - theComponents = np.around(np.dot(basis['proper'],v),12) - inSST = np.all(theComponents >= 0.0) + + b_i = np.broadcast_to(basis['improper'],vector.shape+(3,)) + if proper: + b_p = np.broadcast_to(basis['proper'], vector.shape+(3,)) + improper = np.all(np.around(np.einsum('...ji,...i',b_i,vector),12)>=0.0,axis=-1,keepdims=True) + theComponents = np.where(np.broadcast_to(improper,vector.shape), + np.around(np.einsum('...ji,...i',b_i,vector),12), + np.around(np.einsum('...ji,...i',b_p,vector),12)) else: - v[2] = abs(v[2]) # z component projects identical - theComponents = np.around(np.dot(basis['improper'],v),12) # for positive and negative values - inSST = np.all(theComponents >= 0.0) + vector_ = np.block([vector[...,0:2],np.abs(vector[...,2:3])]) # z component projects identical + theComponents = np.around(np.einsum('...ji,...i',b_i,vector_),12) + + in_SST = np.all(theComponents >= 0.0,axis=-1) if color: # have to return color array - if inSST: - rgb = np.power(theComponents/np.linalg.norm(theComponents),0.5) # smoothen color ramps - rgb = np.minimum(np.ones(3,dtype=float),rgb) # limit to maximum intensity - rgb /= max(rgb) # normalize to (HS)V = 1 - else: - rgb = np.zeros(3,dtype=float) - return (inSST,rgb) + with np.errstate(invalid='ignore',divide='ignore'): + rgb = (theComponents/np.linalg.norm(theComponents,axis=-1,keepdims=True))**0.5 # smoothen color ramps + rgb = np.minimum(1.,rgb) # limit to maximum intensity + rgb /= np.max(rgb,axis=-1,keepdims=True) # normalize to (HS)V = 1 + rgb[np.broadcast_to(~in_SST.reshape(vector[...,0].shape+(1,)),vector.shape)] = 0.0 + return (in_SST,rgb) else: - return inSST - -# code derived from https://github.com/ezag/pyeuclid -# suggested reading: http://web.mit.edu/2.998/www/QuaternionReport1.pdf + return in_SST # ****************************************************************************************** -class Lattice: +class Lattice: # ToDo: Make a subclass of Symmetry! """ - Lattice system. + Bravais lattice. - Currently, this contains only a mapping from Bravais lattice to symmetry + This contains only a mapping from Bravais lattice to symmetry and orientation relationships. It could include twin and slip systems. References @@ -324,15 +321,15 @@ class Lattice: """ lattices = { - 'triclinic':{'symmetry':None}, - 'bct':{'symmetry':'tetragonal'}, - 'hex':{'symmetry':'hexagonal'}, - 'fcc':{'symmetry':'cubic','c/a':1.0}, - 'bcc':{'symmetry':'cubic','c/a':1.0}, + 'triclinic':{'system':None}, + 'bct': {'system':'tetragonal'}, + 'hex': {'system':'hexagonal'}, + 'fcc': {'system':'cubic','c/a':1.0}, + 'bcc': {'system':'cubic','c/a':1.0}, } - def __init__(self, lattice): + def __init__(self,lattice,c_over_a=None): """ New lattice of given type. @@ -343,18 +340,23 @@ class Lattice: """ self.lattice = lattice - self.symmetry = Symmetry(self.lattices[lattice]['symmetry']) + self.symmetry = Symmetry(self.lattices[lattice]['system']) + # transition to subclass + self.system = self.symmetry.system + self.in_SST = self.symmetry.in_SST + self.in_FZ = self.symmetry.in_FZ + self.in_disorientation_SST = self.symmetry.in_disorientation_SST def __repr__(self): """Report basic lattice information.""" - return f'Bravais lattice {self.lattice} ({self.symmetry} symmetry)' + return f'Bravais lattice {self.lattice} ({self.symmetry} crystal system)' # Kurdjomov--Sachs orientation relationship for fcc <-> bcc transformation # from S. Morito et al., Journal of Alloys and Compounds 577:s587-s592, 2013 # 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([ [[ 1, 1, 1],[ 0, 1, 1]], [[ 1, 1, 1],[ 0, 1, 1]], @@ -408,7 +410,7 @@ class Lattice: # Greninger--Troiano orientation relationship for fcc <-> bcc transformation # 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([ [[ 1, 1, 1],[ 1, 0, 1]], [[ 1, 1, 1],[ 1, 1, 0]], @@ -462,7 +464,7 @@ class Lattice: # Greninger--Troiano' orientation relationship for fcc <-> bcc transformation # 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([ [[ 7, 17, 17],[ 12, 5, 17]], [[ 17, 7, 17],[ 17, 12, 5]], @@ -516,7 +518,7 @@ class Lattice: # Nishiyama--Wassermann orientation relationship for fcc <-> bcc transformation # 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([ [[ 1, 1, 1],[ 0, 1, 1]], [[ 1, 1, 1],[ 0, 1, 1]], @@ -546,7 +548,7 @@ class Lattice: # Pitsch orientation relationship for fcc <-> bcc transformation # 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([ [[ 0, 1, 0],[ -1, 0, 1]], [[ 0, 0, 1],[ 1, -1, 0]], @@ -576,7 +578,7 @@ class Lattice: # Bain orientation relationship for fcc <-> bcc transformation # 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([ [[ 1, 0, 0],[ 1, 0, 0]], [[ 0, 1, 0],[ 0, 1, 0]], @@ -586,7 +588,8 @@ class Lattice: [[ 0, 0, 1],[ 1, 0, 1]], [[ 1, 0, 0],[ 1, 1, 0]]],dtype='float')} - def relationOperations(self,model): + + def relation_operations(self,model): """ Crystallographic orientation relationships for phase transformations. @@ -608,8 +611,8 @@ class Lattice: https://doi.org/10.1016/j.actamat.2004.11.021 """ - models={'KS':self.KS, 'GT':self.GT, 'GT_prime':self.GTprime, - 'NW':self.NW, 'Pitsch': self.Pitsch, 'Bain':self.Bain} + models={'KS':self._KS, 'GT':self._GT, 'GT_prime':self._GTprime, + 'NW':self._NW, 'Pitsch': self._Pitsch, 'Bain':self._Bain} try: relationship = models[model] except KeyError : @@ -635,6 +638,8 @@ class Lattice: otherDir = miller[otherDir_id]/ np.linalg.norm(miller[otherDir_id]) otherMatrix = np.array([otherDir,np.cross(otherPlane,otherDir),otherPlane]) - r['rotations'].append(Rotation.from_matrix(np.dot(otherMatrix.T,myMatrix))) + r['rotations'].append(np.dot(otherMatrix.T,myMatrix)) + + r['rotations'] = np.array(r['rotations']) return r diff --git a/python/damask/_orientation.py b/python/damask/_orientation.py index e4dbc7d7c..0bb0e1bc8 100644 --- a/python/damask/_orientation.py +++ b/python/damask/_orientation.py @@ -3,7 +3,7 @@ import numpy as np from . import Lattice from . import Rotation -class Orientation: +class Orientation: # ToDo: make subclass of lattice and Rotation? """ Crystallographic orientation. @@ -39,9 +39,12 @@ class Orientation: else: self.rotation = Rotation.from_quaternion(rotation) # assume quaternion - if self.rotation.quaternion.shape != (4,): - raise NotImplementedError('Support for multiple rotations missing') + def __getitem__(self,item): + """Iterate over leading/leftmost dimension of Orientation array.""" + return self.__class__(self.rotation[item],self.lattice) + + # ToDo: Discuss vectorization/calling signature def disorientation(self, other, SST = True, @@ -58,8 +61,8 @@ class Orientation: if self.lattice.symmetry != other.lattice.symmetry: raise NotImplementedError('disorientation between different symmetry classes not supported yet.') - mySymEqs = self.equivalentOrientations() if SST else self.equivalentOrientations([0]) # take all or only first sym operation - otherSymEqs = other.equivalentOrientations() + mySymEqs = self.equivalent if SST else self.equivalent[0] #ToDo: This is just me! # take all or only first sym operation + otherSymEqs = other.equivalent for i,sA in enumerate(mySymEqs): aInv = sA.rotation.inversed() @@ -68,8 +71,8 @@ class Orientation: r = b*aInv for k in range(2): r.inverse() - breaker = self.lattice.symmetry.inFZ(r.as_Rodrigues(vector=True)) \ - and (not SST or other.lattice.symmetry.inDisorientationSST(r.as_Rodrigues(vector=True))) + breaker = self.lattice.in_FZ(r.as_Rodrigues(vector=True)) \ + and (not SST or other.lattice.in_disorientation_SST(r.as_Rodrigues(vector=True))) if breaker: break if breaker: break if breaker: break @@ -77,79 +80,123 @@ class Orientation: return (Orientation(r,self.lattice), i,j, k == 1) if symmetries else r # disorientation ... # ... own sym, other sym, # self-->other: True, self<--other: False - def inFZ(self): - return self.lattice.symmetry.inFZ(self.rotation.as_Rodrigues(vector=True)) + + @property + def in_FZ(self): + """Check if orientations fall into Fundamental Zone.""" + return self.lattice.in_FZ(self.rotation.as_Rodrigues(vector=True)) - def equivalentOrientations(self,members=[]): - """List of orientations which are symmetrically equivalent.""" - try: - iter(members) # asking for (even empty) list of members? - except TypeError: - return self.__class__(self.lattice.symmetry.symmetryOperations(members)*self.rotation,self.lattice) # no, return rotation object - else: - return [self.__class__(q*self.rotation,self.lattice) \ - for q in self.lattice.symmetry.symmetryOperations(members)] # yes, return list of rotations + @property + def equivalent(self): + """ + Orientations which are symmetrically equivalent. - def relatedOrientations(self,model): - """List of orientations related by the given orientation relationship.""" - r = self.lattice.relationOperations(model) - return [self.__class__(o*self.rotation,r['lattice']) for o in r['rotations']] + One dimension (length according to number of symmetrically equivalent orientations) + is added to the left of the Rotation array. + + """ + o = self.lattice.symmetry.symmetry_operations + o = o.reshape(o.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) + o = Rotation(np.broadcast_to(o,o.shape[:1]+self.rotation.quaternion.shape)) + + s = np.broadcast_to(self.rotation.quaternion,o.shape[:1]+self.rotation.quaternion.shape) + + return self.__class__(o@Rotation(s),self.lattice) + def related(self,model): + """ + Orientations related by the given orientation relationship. + + One dimension (length according to number of related orientations) + is added to the left of the Rotation array. + + """ + o = Rotation.from_matrix(self.lattice.relation_operations(model)['rotations']).as_quaternion() + o = o.reshape(o.shape[:1]+(1,)*len(self.rotation.shape)+(4,)) + o = Rotation(np.broadcast_to(o,o.shape[:1]+self.rotation.quaternion.shape)) + + s = np.broadcast_to(self.rotation.quaternion,o.shape[:1]+self.rotation.quaternion.shape) + + return self.__class__(o@Rotation(s),self.lattice.relation_operations(model)['lattice']) + + + @property def reduced(self): """Transform orientation to fall into fundamental zone according to symmetry.""" - for me in self.equivalentOrientations(): - if self.lattice.symmetry.inFZ(me.rotation.as_Rodrigues(vector=True)): break + eq = self.equivalent + in_FZ = eq.in_FZ - return self.__class__(me.rotation,self.lattice) + # remove duplicates (occur for highly symmetric orientations) + found = np.zeros_like(in_FZ[0],dtype=bool) + q = self.rotation.quaternion[0] + for s in range(in_FZ.shape[0]): + #something fishy... why does q needs to be initialized? + q = np.where(np.expand_dims(np.logical_and(in_FZ[s],~found),-1),eq.rotation.quaternion[s],q) + found = np.logical_or(in_FZ[s],found) + + return self.__class__(q,self.lattice) - def inversePole(self, - axis, - proper = False, - SST = True): + def inverse_pole(self,axis,proper=False,SST=True): """Axis rotated according to orientation (using crystal symmetry to ensure location falls into SST).""" - if SST: # pole requested to be within SST - for i,o in enumerate(self.equivalentOrientations()): # test all symmetric equivalent quaternions - pole = o.rotation*axis # align crystal direction to axis - if self.lattice.symmetry.inSST(pole,proper): break # found SST version + if SST: + eq = self.equivalent + pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) + in_SST = self.lattice.in_SST(pole,proper=proper) + + # remove duplicates (occur for highly symmetric orientations) + found = np.zeros_like(in_SST[0],dtype=bool) + p = pole[0] + for s in range(in_SST.shape[0]): + p = np.where(np.expand_dims(np.logical_and(in_SST[s],~found),-1),pole[s],p) + found = np.logical_or(in_SST[s],found) + + return p else: - pole = self.rotation*axis # align crystal direction to axis - - return (pole,i if SST else 0) + return self.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),self.rotation.shape+(3,)) - def IPFcolor(self,axis): + + def IPF_color(self,axis): #ToDo axis or direction? """TSL color of inverse pole figure for given axis.""" - color = np.zeros(3,'d') + eq = self.equivalent + pole = eq.rotation @ np.broadcast_to(axis/np.linalg.norm(axis),eq.rotation.shape+(3,)) + in_SST, color = self.lattice.in_SST(pole,color=True) - for o in self.equivalentOrientations(): - pole = o.rotation*axis # align crystal direction to axis - inSST,color = self.lattice.symmetry.inSST(pole,color=True) - if inSST: break + # remove duplicates (occur for highly symmetric orientations) + found = np.zeros_like(in_SST[0],dtype=bool) + c = color[0] + for s in range(in_SST.shape[0]): + c = np.where(np.expand_dims(np.logical_and(in_SST[s],~found),-1),color[s],c) + found = np.logical_or(in_SST[s],found) - return color + return c + # ToDo: Discuss vectorization/calling signature @staticmethod - def fromAverage(orientations, + def from_average(orientations, weights = []): """Create orientation from average of list of orientations.""" + # further read: Orientation distribution analysis in deformed grains + # https://doi.org/10.1107/S0021889801003077 if not all(isinstance(item, Orientation) for item in orientations): raise TypeError("Only instances of Orientation can be averaged.") closest = [] ref = orientations[0] for o in orientations: - closest.append(o.equivalentOrientations( + closest.append(o.equivalent[ ref.disorientation(o, SST = False, # select (o[ther]'s) sym orientation - symmetries = True)[2]).rotation) # with lowest misorientation + symmetries = True)[2]].rotation) # with lowest misorientation - return Orientation(Rotation.fromAverage(closest,weights),ref.lattice) + return Orientation(Rotation.from_average(closest,weights),ref.lattice) + # ToDo: Discuss vectorization/calling signature def average(self,other): """Calculate the average rotation.""" - return Orientation.fromAverage([self,other]) + return Orientation.from_average([self,other]) diff --git a/python/damask/_result.py b/python/damask/_result.py index 354c1c3f7..4b32798f1 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -1,4 +1,4 @@ -import multiprocessing +import multiprocessing as mp import re import inspect import glob @@ -11,7 +11,9 @@ from functools import partial import h5py import numpy as np +from numpy.lib import recfunctions as rfn +import damask from . import VTK from . import Table from . import Rotation @@ -20,7 +22,6 @@ from . import Environment from . import grid_filters from . import mechanics from . import util -from . import version class Result: @@ -413,17 +414,18 @@ class Result: for i in self.iterate('increments'): message += f'\n{i} ({self.times[self.increments.index(i)]}s)\n' for o,p in zip(['constituents','materialpoints'],['con_physics','mat_physics']): + message += f' {o[:-1]}\n' for oo in self.iterate(o): - message += f' {oo}\n' + message += f' {oo}\n' for pp in self.iterate(p): - message += f' {pp}\n' + message += f' {pp}\n' group = '/'.join([i,o[:-1],oo,pp]) # o[:-1]: plural/singular issue for d in f[group].keys(): try: dataset = f['/'.join([group,d])] unit = f" / {dataset.attrs['Unit'].decode()}" if 'Unit' in dataset.attrs else '' description = dataset.attrs['Description'].decode() - message += f' {d}{unit}: {description}\n' + message += f' {d}{unit}: {description}\n' except KeyError: pass return message @@ -703,7 +705,6 @@ class Result: label,p = 'intermediate',1 elif eigenvalue == 'min': label,p = 'minimum',0 - print('p',eigenvalue) return { 'data': mechanics.eigenvectors(T_sym['data'])[:,p], 'label': f"v_{eigenvalue}({T_sym['label']})", @@ -731,29 +732,23 @@ class Result: @staticmethod - def _add_IPFcolor(q,l): - d = np.array(l) - d_unit = d/np.linalg.norm(d) - m = util.scale_to_coprime(d) - colors = np.empty((len(q['data']),3),np.uint8) + def _add_IPF_color(q,l): + m = util.scale_to_coprime(np.array(l)) - lattice = q['meta']['Lattice'] - - for i,qu in enumerate(q['data']): - o = Orientation(np.array([qu['w'],qu['x'],qu['y'],qu['z']]),lattice).reduced() - colors[i] = np.uint8(o.IPFcolor(d_unit)*255) + o = Orientation(Rotation(rfn.structured_to_unstructured(q['data'])), + lattice = q['meta']['Lattice']) return { - 'data': colors, + 'data': np.uint8(o.IPF_color(l)*255), 'label': 'IPFcolor_[{} {} {}]'.format(*m), 'meta' : { - 'Unit': 'RGB (8bit)', - 'Lattice': lattice, + 'Unit': '8-bit RGB', + 'Lattice': q['meta']['Lattice'], 'Description': 'Inverse Pole Figure (IPF) colors along sample direction [{} {} {}]'.format(*m), 'Creator': inspect.stack()[0][3][1:] } } - def add_IPFcolor(self,q,l): + def add_IPF_color(self,q,l): """ Add RGB color tuple of inverse pole figure (IPF) color. @@ -765,7 +760,7 @@ class Result: Lab frame direction for inverse pole figure. """ - self._add_generic_pointwise(self._add_IPFcolor,{'q':q},{'l':l}) + self._add_generic_pointwise(self._add_IPF_color,{'q':q},{'l':l}) @staticmethod @@ -1066,8 +1061,8 @@ class Result: """ num_threads = Environment().options['DAMASK_NUM_THREADS'] - pool = multiprocessing.Pool(int(num_threads) if num_threads is not None else None) - lock = multiprocessing.Manager().Lock() + pool = mp.Pool(int(num_threads) if num_threads is not None else None) + lock = mp.Manager().Lock() groups = self.groups_with_datasets(datasets.values()) default_arg = partial(self._job,func=func,datasets=datasets,args=args,lock=lock) @@ -1090,7 +1085,7 @@ class Result: for l,v in result[1]['meta'].items(): dataset.attrs[l]=v.encode() - creator = f"damask.Result.{dataset.attrs['Creator'].decode()} v{version}" + creator = f"damask.Result.{dataset.attrs['Creator'].decode()} v{damask.version}" dataset.attrs['Creator'] = creator.encode() except (OSError,RuntimeError) as err: @@ -1222,7 +1217,7 @@ class Result: elif mode.lower()=='point': v = VTK.from_polyData(self.cell_coordinates()) - N_digits = int(np.floor(np.log10(int(self.increments[-1][3:]))))+1 + N_digits = int(np.floor(np.log10(max(1,int(self.increments[-1][3:])))))+1 for inc in util.show_progress(self.iterate('increments'),len(self.selection['increments'])): diff --git a/python/damask/_rotation.py b/python/damask/_rotation.py index 335d28b3b..9bb2d300a 100644 --- a/python/damask/_rotation.py +++ b/python/damask/_rotation.py @@ -53,6 +53,8 @@ class Rotation: Use .from_quaternion to perform a sanity check. """ + if quaternion.shape[-1] != 4: + raise ValueError('Not a quaternion') self.quaternion = quaternion.copy() @@ -61,6 +63,7 @@ class Rotation: return self.quaternion.shape[:-1] + # ToDo: Check difference __copy__ vs __deepcopy__ def __copy__(self): """Copy.""" return self.__class__(self.quaternion) @@ -71,7 +74,7 @@ class Rotation: def __repr__(self): """Orientation displayed as unit quaternion, rotation matrix, and Bunge-Euler angles.""" if self.quaternion.shape != (4,): - raise NotImplementedError('Support for multiple rotations missing') + return 'Quaternions:\n'+str(self.quaternion) # ToDo: could be nicer ... return '\n'.join([ 'Quaternion: (real={:.3f}, imag=<{:+.3f}, {:+.3f}, {:+.3f}>)'.format(*(self.quaternion)), 'Matrix:\n{}'.format(np.round(self.as_matrix(),8)), @@ -79,6 +82,19 @@ class Rotation: ]) + def __getitem__(self,item): + """Iterate over leading/leftmost dimension of Rotation array.""" + if self.shape == (): return self.copy() + if isinstance(item,tuple) and len(item) >= len(self): + raise IndexError('Too many indices') + return self.__class__(self.quaternion[item]) + + + def __len__(self): + """Length of leading/leftmost dimension of Rotation array.""" + return 0 if self.shape == () else self.shape[0] + + def __matmul__(self, other): """ Rotation of vector, second or fourth order tensor, or rotation object. @@ -88,6 +104,11 @@ class Rotation: other : numpy.ndarray or Rotation Vector, second or fourth order tensor, or rotation object that is rotated. + Returns + ------- + other_rot : numpy.ndarray or Rotation + Rotated vector, second or fourth order tensor, or rotation object. + """ if isinstance(other, Rotation): q_m = self.quaternion[...,0:1] @@ -155,7 +176,7 @@ class Rotation: def broadcast_to(self,shape): - if isinstance(shape,int): shape = (shape,) + if isinstance(shape,(int,np.integer)): shape = (shape,) if self.shape == (): q = np.broadcast_to(self.quaternion,shape+(4,)) else: @@ -178,7 +199,7 @@ class Rotation: """ if self.quaternion.shape != (4,) or other.quaternion.shape != (4,): raise NotImplementedError('Support for multiple rotations missing') - return Rotation.fromAverage([self,other]) + return Rotation.from_average([self,other]) ################################################################################################ @@ -273,7 +294,11 @@ class Rotation: """ ro = Rotation._qu2ro(self.quaternion) - return ro[...,:3]*ro[...,3] if vector else ro + if vector: + with np.errstate(invalid='ignore'): + return ro[...,:3]*ro[...,3:4] + else: + return ro def as_homochoric(self): """ @@ -299,6 +324,7 @@ class Rotation: """ return Rotation._qu2cu(self.quaternion) + @property def M(self): # ToDo not sure about the name: as_M or M? we do not have a from_M """ Intermediate representation supporting quaternion averaging. @@ -555,7 +581,7 @@ class Rotation: @staticmethod - def fromAverage(rotations,weights = None): + def from_average(rotations,weights = None): """ Average rotation. @@ -580,8 +606,8 @@ class Rotation: weights = np.ones(N,dtype='i') for i,(r,n) in enumerate(zip(rotations,weights)): - M = r.M() * n if i == 0 \ - else M + r.M() * n # noqa add (multiples) of this rotation to average noqa + M = r.M * n if i == 0 \ + else M + r.M * n # noqa add (multiples) of this rotation to average noqa eig, vec = np.linalg.eig(M/N) return Rotation.from_quaternion(np.real(vec.T[eig.argmax()]),accept_homomorph = True) @@ -593,7 +619,7 @@ class Rotation: elif hasattr(shape, '__iter__'): r = np.random.random(tuple(shape)+(3,)) else: - r = np.random.random((shape,3)) + r = np.random.rand(shape,3) A = np.sqrt(r[...,2]) B = np.sqrt(1.0-r[...,2]) @@ -604,11 +630,9 @@ class Rotation: return Rotation(q.reshape(r.shape[:-1]+(4,)) if shape is not None else q)._standardize() - # for compatibility (old names do not follow convention) - asM = M - fromQuaternion = from_quaternion fromEulers = from_Eulers + fromQuaternion = from_quaternion asAxisAngle = as_axis_angle __mul__ = __matmul__ diff --git a/python/damask/_table.py b/python/damask/_table.py index 435b7afa9..e8d35c545 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -19,11 +19,12 @@ class Table: Data. Column labels from a pandas.DataFrame will be replaced. shapes : dict with str:tuple pairs Shapes of the columns. Example 'F':(3,3) for a deformation gradient. - comments : iterable of str, optional + comments : str or iterable of str, optional Additional, human-readable information. """ - self.comments = [] if comments is None else [c for c in comments] + comments_ = [comments] if isinstance(comments,str) else comments + self.comments = [] if comments_ is None else [c for c in comments_] self.data = pd.DataFrame(data=data) self.shapes = { k:(v,) if isinstance(v,(np.int,int)) else v for k,v in shapes.items() } self._label_condensed() diff --git a/python/damask/_vtk.py b/python/damask/_vtk.py index 9a34800dc..656d68412 100644 --- a/python/damask/_vtk.py +++ b/python/damask/_vtk.py @@ -1,3 +1,4 @@ +import multiprocessing as mp from pathlib import Path import pandas as pd @@ -6,9 +7,9 @@ import vtk from vtk.util.numpy_support import numpy_to_vtk as np_to_vtk from vtk.util.numpy_support import numpy_to_vtkIdTypeArray as np_to_vtkIdTypeArray +import damask from . import Table from . import Environment -from . import version class VTK: @@ -159,8 +160,11 @@ class VTK: return VTK(geom) - - def write(self,fname): + @staticmethod + def _write(writer): + """Wrapper for parallel writing.""" + writer.Write() + def write(self,fname,parallel=True): """ Write to file. @@ -168,6 +172,8 @@ class VTK: ---------- fname : str or pathlib.Path Filename for writing. + parallel : boolean, optional + Write data in parallel background process. Defaults to True. """ if isinstance(self.geom,vtk.vtkRectilinearGrid): @@ -185,8 +191,11 @@ class VTK: writer.SetCompressorTypeToZLib() writer.SetDataModeToBinary() writer.SetInputData(self.geom) - - writer.Write() + if parallel: + mp_writer = mp.Process(target=self._write,args=(writer,)) + mp_writer.start() + else: + writer.Write() # Check https://blog.kitware.com/ghost-and-blanking-visibility-changes/ for missing data @@ -197,14 +206,21 @@ class VTK: N_cells = self.geom.GetNumberOfCells() if isinstance(data,np.ndarray): - d = np_to_vtk(num_array=data.reshape(data.shape[0],-1),deep=True) if label is None: raise ValueError('No label defined for numpy.ndarray') + + if data.dtype in [np.float64, np.float128]: # avoid large files + d = np_to_vtk(num_array=data.astype(np.float32).reshape(data.shape[0],-1),deep=True) + else: + d = np_to_vtk(num_array=data.reshape(data.shape[0],-1),deep=True) d.SetName(label) + if data.shape[0] == N_cells: self.geom.GetCellData().AddArray(d) elif data.shape[0] == N_points: self.geom.GetPointData().AddArray(d) + else: + raise ValueError(f'Invalid shape {data.shape[0]}') elif isinstance(data,pd.DataFrame): raise NotImplementedError('pd.DataFrame') elif isinstance(data,Table): @@ -216,7 +232,7 @@ class VTK: def __repr__(self): """ASCII representation of the VTK data.""" writer = vtk.vtkDataSetWriter() - writer.SetHeader(f'# DAMASK.VTK v{version}') + writer.SetHeader(f'# damask.VTK v{damask.version}') writer.WriteToOutputStringOn() writer.SetInputData(self.geom) writer.Write() @@ -242,7 +258,7 @@ class VTK: ren.AddActor(actor) ren.SetBackground(0.2,0.2,0.2) - window.SetSize(Environment().screen_width,Environment().screen_height) + window.SetSize(Environment().screen_size[0],Environment().screen_size[1]) iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(window) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 2a46bc815..97c8ee44e 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -1,7 +1,13 @@ from pathlib import Path +import numpy as np import pytest +# Use to monkeypatch damask.version (for comparsion to reference files that contain version information) +def pytest_configure(): + pytest.dummy_version = '99.99.99-9999-pytest' + + def pytest_addoption(parser): parser.addoption("--update", action="store_true", @@ -16,3 +22,92 @@ def update(request): def reference_dir_base(): """Directory containing reference results.""" return Path(__file__).parent/'reference' + +@pytest.fixture +def set_of_quaternions(): + """A set of n random rotations.""" + def random_quaternions(N): + r = np.random.rand(N,3) + + A = np.sqrt(r[:,2]) + B = np.sqrt(1.0-r[:,2]) + qu = np.column_stack([np.cos(2.0*np.pi*r[:,0])*A, + np.sin(2.0*np.pi*r[:,1])*B, + np.cos(2.0*np.pi*r[:,1])*B, + np.sin(2.0*np.pi*r[:,0])*A]) + qu[:,0]*=np.sign(qu[:,0]) + + return qu + + n = 1100 + scatter=1.e-2 + specials = np.array([ + [1.0, 0.0, 0.0, 0.0], + #---------------------- + [0.0, 1.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 1.0], + [0.0,-1.0, 0.0, 0.0], + [0.0, 0.0,-1.0, 0.0], + [0.0, 0.0, 0.0,-1.0], + #---------------------- + [1.0, 1.0, 0.0, 0.0], + [1.0, 0.0, 1.0, 0.0], + [1.0, 0.0, 0.0, 1.0], + [0.0, 1.0, 1.0, 0.0], + [0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 1.0], + #---------------------- + [1.0,-1.0, 0.0, 0.0], + [1.0, 0.0,-1.0, 0.0], + [1.0, 0.0, 0.0,-1.0], + [0.0, 1.0,-1.0, 0.0], + [0.0, 1.0, 0.0,-1.0], + [0.0, 0.0, 1.0,-1.0], + #---------------------- + [0.0, 1.0,-1.0, 0.0], + [0.0, 1.0, 0.0,-1.0], + [0.0, 0.0, 1.0,-1.0], + #---------------------- + [0.0,-1.0,-1.0, 0.0], + [0.0,-1.0, 0.0,-1.0], + [0.0, 0.0,-1.0,-1.0], + #---------------------- + [1.0, 1.0, 1.0, 0.0], + [1.0, 1.0, 0.0, 1.0], + [1.0, 0.0, 1.0, 1.0], + [1.0,-1.0, 1.0, 0.0], + [1.0,-1.0, 0.0, 1.0], + [1.0, 0.0,-1.0, 1.0], + [1.0, 1.0,-1.0, 0.0], + [1.0, 1.0, 0.0,-1.0], + [1.0, 0.0, 1.0,-1.0], + [1.0,-1.0,-1.0, 0.0], + [1.0,-1.0, 0.0,-1.0], + [1.0, 0.0,-1.0,-1.0], + #---------------------- + [0.0, 1.0, 1.0, 1.0], + [0.0, 1.0,-1.0, 1.0], + [0.0, 1.0, 1.0,-1.0], + [0.0,-1.0, 1.0, 1.0], + [0.0,-1.0,-1.0, 1.0], + [0.0,-1.0, 1.0,-1.0], + [0.0,-1.0,-1.0,-1.0], + #---------------------- + [1.0, 1.0, 1.0, 1.0], + [1.0,-1.0, 1.0, 1.0], + [1.0, 1.0,-1.0, 1.0], + [1.0, 1.0, 1.0,-1.0], + [1.0,-1.0,-1.0, 1.0], + [1.0,-1.0, 1.0,-1.0], + [1.0, 1.0,-1.0,-1.0], + [1.0,-1.0,-1.0,-1.0], + ]) + specials /= np.linalg.norm(specials,axis=1).reshape(-1,1) + specials_scatter = specials + np.broadcast_to(np.random.rand(4)*scatter,specials.shape) + specials_scatter /= np.linalg.norm(specials_scatter,axis=1).reshape(-1,1) + specials_scatter[specials_scatter[:,0]<0]*=-1 + + return np.array([s for s in specials] + \ + [s for s in specials_scatter] + \ + [s for s in random_quaternions(n-2*len(specials))]) diff --git a/python/tests/reference/Colormap/binary.json b/python/tests/reference/Colormap/binary.json new file mode 100644 index 000000000..c71d3649b --- /dev/null +++ b/python/tests/reference/Colormap/binary.json @@ -0,0 +1,1290 @@ +[ + { + "Creator": "damask.Colormap v99.99.99-9999-pytest", + "ColorSpace": "RGB", + "Name": "binary", + "DefaultMap": true, + "RGBPoints": [ + 0, + 1.0, + 1.0, + 1.0, + 1.0, + 1, + 0.996078, + 0.996078, + 0.996078, + 1.0, + 2, + 0.992157, + 0.992157, + 0.992157, + 1.0, + 3, + 0.988235, + 0.988235, + 0.988235, + 1.0, + 4, + 0.984314, + 0.984314, + 0.984314, + 1.0, + 5, + 0.980392, + 0.980392, + 0.980392, + 1.0, + 6, + 0.976471, + 0.976471, + 0.976471, + 1.0, + 7, + 0.972549, + 0.972549, + 0.972549, + 1.0, + 8, + 0.968627, + 0.968627, + 0.968627, + 1.0, + 9, + 0.964706, + 0.964706, + 0.964706, + 1.0, + 10, + 0.960784, + 0.960784, + 0.960784, + 1.0, + 11, + 0.956863, + 0.956863, + 0.956863, + 1.0, + 12, + 0.952941, + 0.952941, + 0.952941, + 1.0, + 13, + 0.94902, + 0.94902, + 0.94902, + 1.0, + 14, + 0.945098, + 0.945098, + 0.945098, + 1.0, + 15, + 0.941176, + 0.941176, + 0.941176, + 1.0, + 16, + 0.937255, + 0.937255, + 0.937255, + 1.0, + 17, + 0.933333, + 0.933333, + 0.933333, + 1.0, + 18, + 0.929412, + 0.929412, + 0.929412, + 1.0, + 19, + 0.92549, + 0.92549, + 0.92549, + 1.0, + 20, + 0.921569, + 0.921569, + 0.921569, + 1.0, + 21, + 0.917647, + 0.917647, + 0.917647, + 1.0, + 22, + 0.913725, + 0.913725, + 0.913725, + 1.0, + 23, + 0.909804, + 0.909804, + 0.909804, + 1.0, + 24, + 0.905882, + 0.905882, + 0.905882, + 1.0, + 25, + 0.901961, + 0.901961, + 0.901961, + 1.0, + 26, + 0.898039, + 0.898039, + 0.898039, + 1.0, + 27, + 0.894118, + 0.894118, + 0.894118, + 1.0, + 28, + 0.890196, + 0.890196, + 0.890196, + 1.0, + 29, + 0.886275, + 0.886275, + 0.886275, + 1.0, + 30, + 0.882353, + 0.882353, + 0.882353, + 1.0, + 31, + 0.878431, + 0.878431, + 0.878431, + 1.0, + 32, + 0.87451, + 0.87451, + 0.87451, + 1.0, + 33, + 0.870588, + 0.870588, + 0.870588, + 1.0, + 34, + 0.866667, + 0.866667, + 0.866667, + 1.0, + 35, + 0.862745, + 0.862745, + 0.862745, + 1.0, + 36, + 0.858824, + 0.858824, + 0.858824, + 1.0, + 37, + 0.854902, + 0.854902, + 0.854902, + 1.0, + 38, + 0.85098, + 0.85098, + 0.85098, + 1.0, + 39, + 0.847059, + 0.847059, + 0.847059, + 1.0, + 40, + 0.843137, + 0.843137, + 0.843137, + 1.0, + 41, + 0.839216, + 0.839216, + 0.839216, + 1.0, + 42, + 0.835294, + 0.835294, + 0.835294, + 1.0, + 43, + 0.831373, + 0.831373, + 0.831373, + 1.0, + 44, + 0.827451, + 0.827451, + 0.827451, + 1.0, + 45, + 0.823529, + 0.823529, + 0.823529, + 1.0, + 46, + 0.819608, + 0.819608, + 0.819608, + 1.0, + 47, + 0.815686, + 0.815686, + 0.815686, + 1.0, + 48, + 0.811765, + 0.811765, + 0.811765, + 1.0, + 49, + 0.807843, + 0.807843, + 0.807843, + 1.0, + 50, + 0.803922, + 0.803922, + 0.803922, + 1.0, + 51, + 0.8, + 0.8, + 0.8, + 1.0, + 52, + 0.796078, + 0.796078, + 0.796078, + 1.0, + 53, + 0.792157, + 0.792157, + 0.792157, + 1.0, + 54, + 0.788235, + 0.788235, + 0.788235, + 1.0, + 55, + 0.784314, + 0.784314, + 0.784314, + 1.0, + 56, + 0.780392, + 0.780392, + 0.780392, + 1.0, + 57, + 0.776471, + 0.776471, + 0.776471, + 1.0, + 58, + 0.772549, + 0.772549, + 0.772549, + 1.0, + 59, + 0.768627, + 0.768627, + 0.768627, + 1.0, + 60, + 0.764706, + 0.764706, + 0.764706, + 1.0, + 61, + 0.760784, + 0.760784, + 0.760784, + 1.0, + 62, + 0.756863, + 0.756863, + 0.756863, + 1.0, + 63, + 0.752941, + 0.752941, + 0.752941, + 1.0, + 64, + 0.74902, + 0.74902, + 0.74902, + 1.0, + 65, + 0.745098, + 0.745098, + 0.745098, + 1.0, + 66, + 0.741176, + 0.741176, + 0.741176, + 1.0, + 67, + 0.737255, + 0.737255, + 0.737255, + 1.0, + 68, + 0.733333, + 0.733333, + 0.733333, + 1.0, + 69, + 0.729412, + 0.729412, + 0.729412, + 1.0, + 70, + 0.72549, + 0.72549, + 0.72549, + 1.0, + 71, + 0.721569, + 0.721569, + 0.721569, + 1.0, + 72, + 0.717647, + 0.717647, + 0.717647, + 1.0, + 73, + 0.713725, + 0.713725, + 0.713725, + 1.0, + 74, + 0.709804, + 0.709804, + 0.709804, + 1.0, + 75, + 0.705882, + 0.705882, + 0.705882, + 1.0, + 76, + 0.701961, + 0.701961, + 0.701961, + 1.0, + 77, + 0.698039, + 0.698039, + 0.698039, + 1.0, + 78, + 0.694118, + 0.694118, + 0.694118, + 1.0, + 79, + 0.690196, + 0.690196, + 0.690196, + 1.0, + 80, + 0.686275, + 0.686275, + 0.686275, + 1.0, + 81, + 0.682353, + 0.682353, + 0.682353, + 1.0, + 82, + 0.678431, + 0.678431, + 0.678431, + 1.0, + 83, + 0.67451, + 0.67451, + 0.67451, + 1.0, + 84, + 0.670588, + 0.670588, + 0.670588, + 1.0, + 85, + 0.666667, + 0.666667, + 0.666667, + 1.0, + 86, + 0.662745, + 0.662745, + 0.662745, + 1.0, + 87, + 0.658824, + 0.658824, + 0.658824, + 1.0, + 88, + 0.654902, + 0.654902, + 0.654902, + 1.0, + 89, + 0.65098, + 0.65098, + 0.65098, + 1.0, + 90, + 0.647059, + 0.647059, + 0.647059, + 1.0, + 91, + 0.643137, + 0.643137, + 0.643137, + 1.0, + 92, + 0.639216, + 0.639216, + 0.639216, + 1.0, + 93, + 0.635294, + 0.635294, + 0.635294, + 1.0, + 94, + 0.631373, + 0.631373, + 0.631373, + 1.0, + 95, + 0.627451, + 0.627451, + 0.627451, + 1.0, + 96, + 0.623529, + 0.623529, + 0.623529, + 1.0, + 97, + 0.619608, + 0.619608, + 0.619608, + 1.0, + 98, + 0.615686, + 0.615686, + 0.615686, + 1.0, + 99, + 0.611765, + 0.611765, + 0.611765, + 1.0, + 100, + 0.607843, + 0.607843, + 0.607843, + 1.0, + 101, + 0.603922, + 0.603922, + 0.603922, + 1.0, + 102, + 0.6, + 0.6, + 0.6, + 1.0, + 103, + 0.596078, + 0.596078, + 0.596078, + 1.0, + 104, + 0.592157, + 0.592157, + 0.592157, + 1.0, + 105, + 0.588235, + 0.588235, + 0.588235, + 1.0, + 106, + 0.584314, + 0.584314, + 0.584314, + 1.0, + 107, + 0.580392, + 0.580392, + 0.580392, + 1.0, + 108, + 0.576471, + 0.576471, + 0.576471, + 1.0, + 109, + 0.572549, + 0.572549, + 0.572549, + 1.0, + 110, + 0.568627, + 0.568627, + 0.568627, + 1.0, + 111, + 0.564706, + 0.564706, + 0.564706, + 1.0, + 112, + 0.560784, + 0.560784, + 0.560784, + 1.0, + 113, + 0.556863, + 0.556863, + 0.556863, + 1.0, + 114, + 0.552941, + 0.552941, + 0.552941, + 1.0, + 115, + 0.54902, + 0.54902, + 0.54902, + 1.0, + 116, + 0.545098, + 0.545098, + 0.545098, + 1.0, + 117, + 0.541176, + 0.541176, + 0.541176, + 1.0, + 118, + 0.537255, + 0.537255, + 0.537255, + 1.0, + 119, + 0.533333, + 0.533333, + 0.533333, + 1.0, + 120, + 0.529412, + 0.529412, + 0.529412, + 1.0, + 121, + 0.52549, + 0.52549, + 0.52549, + 1.0, + 122, + 0.521569, + 0.521569, + 0.521569, + 1.0, + 123, + 0.517647, + 0.517647, + 0.517647, + 1.0, + 124, + 0.513725, + 0.513725, + 0.513725, + 1.0, + 125, + 0.509804, + 0.509804, + 0.509804, + 1.0, + 126, + 0.505882, + 0.505882, + 0.505882, + 1.0, + 127, + 0.501961, + 0.501961, + 0.501961, + 1.0, + 128, + 0.498039, + 0.498039, + 0.498039, + 1.0, + 129, + 0.494118, + 0.494118, + 0.494118, + 1.0, + 130, + 0.490196, + 0.490196, + 0.490196, + 1.0, + 131, + 0.486275, + 0.486275, + 0.486275, + 1.0, + 132, + 0.482353, + 0.482353, + 0.482353, + 1.0, + 133, + 0.478431, + 0.478431, + 0.478431, + 1.0, + 134, + 0.47451, + 0.47451, + 0.47451, + 1.0, + 135, + 0.470588, + 0.470588, + 0.470588, + 1.0, + 136, + 0.466667, + 0.466667, + 0.466667, + 1.0, + 137, + 0.462745, + 0.462745, + 0.462745, + 1.0, + 138, + 0.458824, + 0.458824, + 0.458824, + 1.0, + 139, + 0.454902, + 0.454902, + 0.454902, + 1.0, + 140, + 0.45098, + 0.45098, + 0.45098, + 1.0, + 141, + 0.447059, + 0.447059, + 0.447059, + 1.0, + 142, + 0.443137, + 0.443137, + 0.443137, + 1.0, + 143, + 0.439216, + 0.439216, + 0.439216, + 1.0, + 144, + 0.435294, + 0.435294, + 0.435294, + 1.0, + 145, + 0.431373, + 0.431373, + 0.431373, + 1.0, + 146, + 0.427451, + 0.427451, + 0.427451, + 1.0, + 147, + 0.423529, + 0.423529, + 0.423529, + 1.0, + 148, + 0.419608, + 0.419608, + 0.419608, + 1.0, + 149, + 0.415686, + 0.415686, + 0.415686, + 1.0, + 150, + 0.411765, + 0.411765, + 0.411765, + 1.0, + 151, + 0.407843, + 0.407843, + 0.407843, + 1.0, + 152, + 0.403922, + 0.403922, + 0.403922, + 1.0, + 153, + 0.4, + 0.4, + 0.4, + 1.0, + 154, + 0.396078, + 0.396078, + 0.396078, + 1.0, + 155, + 0.392157, + 0.392157, + 0.392157, + 1.0, + 156, + 0.388235, + 0.388235, + 0.388235, + 1.0, + 157, + 0.384314, + 0.384314, + 0.384314, + 1.0, + 158, + 0.380392, + 0.380392, + 0.380392, + 1.0, + 159, + 0.376471, + 0.376471, + 0.376471, + 1.0, + 160, + 0.372549, + 0.372549, + 0.372549, + 1.0, + 161, + 0.368627, + 0.368627, + 0.368627, + 1.0, + 162, + 0.364706, + 0.364706, + 0.364706, + 1.0, + 163, + 0.360784, + 0.360784, + 0.360784, + 1.0, + 164, + 0.356863, + 0.356863, + 0.356863, + 1.0, + 165, + 0.352941, + 0.352941, + 0.352941, + 1.0, + 166, + 0.34902, + 0.34902, + 0.34902, + 1.0, + 167, + 0.345098, + 0.345098, + 0.345098, + 1.0, + 168, + 0.341176, + 0.341176, + 0.341176, + 1.0, + 169, + 0.337255, + 0.337255, + 0.337255, + 1.0, + 170, + 0.333333, + 0.333333, + 0.333333, + 1.0, + 171, + 0.329412, + 0.329412, + 0.329412, + 1.0, + 172, + 0.32549, + 0.32549, + 0.32549, + 1.0, + 173, + 0.321569, + 0.321569, + 0.321569, + 1.0, + 174, + 0.317647, + 0.317647, + 0.317647, + 1.0, + 175, + 0.313725, + 0.313725, + 0.313725, + 1.0, + 176, + 0.309804, + 0.309804, + 0.309804, + 1.0, + 177, + 0.305882, + 0.305882, + 0.305882, + 1.0, + 178, + 0.301961, + 0.301961, + 0.301961, + 1.0, + 179, + 0.298039, + 0.298039, + 0.298039, + 1.0, + 180, + 0.294118, + 0.294118, + 0.294118, + 1.0, + 181, + 0.290196, + 0.290196, + 0.290196, + 1.0, + 182, + 0.286275, + 0.286275, + 0.286275, + 1.0, + 183, + 0.282353, + 0.282353, + 0.282353, + 1.0, + 184, + 0.278431, + 0.278431, + 0.278431, + 1.0, + 185, + 0.27451, + 0.27451, + 0.27451, + 1.0, + 186, + 0.270588, + 0.270588, + 0.270588, + 1.0, + 187, + 0.266667, + 0.266667, + 0.266667, + 1.0, + 188, + 0.262745, + 0.262745, + 0.262745, + 1.0, + 189, + 0.258824, + 0.258824, + 0.258824, + 1.0, + 190, + 0.254902, + 0.254902, + 0.254902, + 1.0, + 191, + 0.25098, + 0.25098, + 0.25098, + 1.0, + 192, + 0.247059, + 0.247059, + 0.247059, + 1.0, + 193, + 0.243137, + 0.243137, + 0.243137, + 1.0, + 194, + 0.239216, + 0.239216, + 0.239216, + 1.0, + 195, + 0.235294, + 0.235294, + 0.235294, + 1.0, + 196, + 0.231373, + 0.231373, + 0.231373, + 1.0, + 197, + 0.227451, + 0.227451, + 0.227451, + 1.0, + 198, + 0.223529, + 0.223529, + 0.223529, + 1.0, + 199, + 0.219608, + 0.219608, + 0.219608, + 1.0, + 200, + 0.215686, + 0.215686, + 0.215686, + 1.0, + 201, + 0.211765, + 0.211765, + 0.211765, + 1.0, + 202, + 0.207843, + 0.207843, + 0.207843, + 1.0, + 203, + 0.203922, + 0.203922, + 0.203922, + 1.0, + 204, + 0.2, + 0.2, + 0.2, + 1.0, + 205, + 0.196078, + 0.196078, + 0.196078, + 1.0, + 206, + 0.192157, + 0.192157, + 0.192157, + 1.0, + 207, + 0.188235, + 0.188235, + 0.188235, + 1.0, + 208, + 0.184314, + 0.184314, + 0.184314, + 1.0, + 209, + 0.180392, + 0.180392, + 0.180392, + 1.0, + 210, + 0.176471, + 0.176471, + 0.176471, + 1.0, + 211, + 0.172549, + 0.172549, + 0.172549, + 1.0, + 212, + 0.168627, + 0.168627, + 0.168627, + 1.0, + 213, + 0.164706, + 0.164706, + 0.164706, + 1.0, + 214, + 0.160784, + 0.160784, + 0.160784, + 1.0, + 215, + 0.156863, + 0.156863, + 0.156863, + 1.0, + 216, + 0.152941, + 0.152941, + 0.152941, + 1.0, + 217, + 0.14902, + 0.14902, + 0.14902, + 1.0, + 218, + 0.145098, + 0.145098, + 0.145098, + 1.0, + 219, + 0.141176, + 0.141176, + 0.141176, + 1.0, + 220, + 0.137255, + 0.137255, + 0.137255, + 1.0, + 221, + 0.133333, + 0.133333, + 0.133333, + 1.0, + 222, + 0.129412, + 0.129412, + 0.129412, + 1.0, + 223, + 0.12549, + 0.12549, + 0.12549, + 1.0, + 224, + 0.121569, + 0.121569, + 0.121569, + 1.0, + 225, + 0.117647, + 0.117647, + 0.117647, + 1.0, + 226, + 0.113725, + 0.113725, + 0.113725, + 1.0, + 227, + 0.109804, + 0.109804, + 0.109804, + 1.0, + 228, + 0.105882, + 0.105882, + 0.105882, + 1.0, + 229, + 0.101961, + 0.101961, + 0.101961, + 1.0, + 230, + 0.098039, + 0.098039, + 0.098039, + 1.0, + 231, + 0.094118, + 0.094118, + 0.094118, + 1.0, + 232, + 0.090196, + 0.090196, + 0.090196, + 1.0, + 233, + 0.086275, + 0.086275, + 0.086275, + 1.0, + 234, + 0.082353, + 0.082353, + 0.082353, + 1.0, + 235, + 0.078431, + 0.078431, + 0.078431, + 1.0, + 236, + 0.07451, + 0.07451, + 0.07451, + 1.0, + 237, + 0.070588, + 0.070588, + 0.070588, + 1.0, + 238, + 0.066667, + 0.066667, + 0.066667, + 1.0, + 239, + 0.062745, + 0.062745, + 0.062745, + 1.0, + 240, + 0.058824, + 0.058824, + 0.058824, + 1.0, + 241, + 0.054902, + 0.054902, + 0.054902, + 1.0, + 242, + 0.05098, + 0.05098, + 0.05098, + 1.0, + 243, + 0.047059, + 0.047059, + 0.047059, + 1.0, + 244, + 0.043137, + 0.043137, + 0.043137, + 1.0, + 245, + 0.039216, + 0.039216, + 0.039216, + 1.0, + 246, + 0.035294, + 0.035294, + 0.035294, + 1.0, + 247, + 0.031373, + 0.031373, + 0.031373, + 1.0, + 248, + 0.027451, + 0.027451, + 0.027451, + 1.0, + 249, + 0.023529, + 0.023529, + 0.023529, + 1.0, + 250, + 0.019608, + 0.019608, + 0.019608, + 1.0, + 251, + 0.015686, + 0.015686, + 0.015686, + 1.0, + 252, + 0.011765, + 0.011765, + 0.011765, + 1.0, + 253, + 0.007843, + 0.007843, + 0.007843, + 1.0, + 254, + 0.003922, + 0.003922, + 0.003922, + 1.0, + 255, + 0.0, + 0.0, + 0.0, + 1.0 + ] + } +] \ No newline at end of file diff --git a/python/tests/reference/Colormap/binary.legend b/python/tests/reference/Colormap/binary.legend new file mode 100644 index 000000000..ba417facc --- /dev/null +++ b/python/tests/reference/Colormap/binary.legend @@ -0,0 +1 @@ +1 1 binary 9 binary 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 256 0 0 0 0 255 1 0 0 0 0 255 1 0 1 1 1 255 1 0 2 2 2 255 1 0 4 4 4 255 1 0 5 5 5 255 1 0 6 6 6 255 1 0 7 7 7 255 1 0 8 8 8 255 1 0 8 8 8 255 1 0 9 9 9 255 1 0 11 11 11 255 1 0 12 12 12 255 1 0 13 13 13 255 1 0 14 14 14 255 1 0 15 15 15 255 1 0 16 16 16 255 1 0 16 16 16 255 1 0 17 17 17 255 1 0 18 18 18 255 1 0 20 20 20 255 1 0 21 21 21 255 1 0 22 22 22 255 1 0 23 23 23 255 1 0 24 24 24 255 1 0 24 24 24 255 1 0 25 25 25 255 1 0 27 27 27 255 1 0 28 28 28 255 1 0 29 29 29 255 1 0 30 30 30 255 1 0 31 31 31 255 1 0 32 32 32 255 1 0 32 32 32 255 1 0 33 33 33 255 1 0 34 34 34 255 1 0 36 36 36 255 1 0 37 37 37 255 1 0 38 38 38 255 1 0 39 39 39 255 1 0 40 40 40 255 1 0 40 40 40 255 1 0 41 41 41 255 1 0 43 43 43 255 1 0 44 44 44 255 1 0 45 45 45 255 1 0 46 46 46 255 1 0 47 47 47 255 1 0 48 48 48 255 1 0 48 48 48 255 1 0 49 49 49 255 1 0 50 50 50 255 1 0 52 52 52 255 1 0 53 53 53 255 1 0 54 54 54 255 1 0 55 55 55 255 1 0 56 56 56 255 1 0 56 56 56 255 1 0 57 57 57 255 1 0 59 59 59 255 1 0 60 60 60 255 1 0 61 61 61 255 1 0 62 62 62 255 1 0 63 63 63 255 1 0 64 64 64 255 1 0 65 65 65 255 1 0 65 65 65 255 1 0 66 66 66 255 1 0 68 68 68 255 1 0 69 69 69 255 1 0 70 70 70 255 1 0 71 71 71 255 1 0 72 72 72 255 1 0 73 73 73 255 1 0 73 73 73 255 1 0 75 75 75 255 1 0 76 76 76 255 1 0 77 77 77 255 1 0 78 78 78 255 1 0 79 79 79 255 1 0 80 80 80 255 1 0 81 81 81 255 1 0 81 81 81 255 1 0 82 82 82 255 1 0 84 84 84 255 1 0 85 85 85 255 1 0 86 86 86 255 1 0 87 87 87 255 1 0 88 88 88 255 1 0 89 89 89 255 1 0 89 89 89 255 1 0 91 91 91 255 1 0 92 92 92 255 1 0 93 93 93 255 1 0 94 94 94 255 1 0 95 95 95 255 1 0 96 96 96 255 1 0 97 97 97 255 1 0 97 97 97 255 1 0 98 98 98 255 1 0 100 100 100 255 1 0 101 101 101 255 1 0 102 102 102 255 1 0 103 103 103 255 1 0 104 104 104 255 1 0 105 105 105 255 1 0 105 105 105 255 1 0 107 107 107 255 1 0 108 108 108 255 1 0 109 109 109 255 1 0 110 110 110 255 1 0 111 111 111 255 1 0 112 112 112 255 1 0 113 113 113 255 1 0 113 113 113 255 1 0 114 114 114 255 1 0 116 116 116 255 1 0 117 117 117 255 1 0 118 118 118 255 1 0 119 119 119 255 1 0 120 120 120 255 1 0 121 121 121 255 1 0 121 121 121 255 1 0 123 123 123 255 1 0 124 124 124 255 1 0 125 125 125 255 1 0 126 126 126 255 1 0 127 127 127 255 1 0 128 128 128 255 1 0 129 129 129 255 1 0 130 130 130 255 1 0 131 131 131 255 1 0 131 131 131 255 1 0 133 133 133 255 1 0 134 134 134 255 1 0 135 135 135 255 1 0 136 136 136 255 1 0 137 137 137 255 1 0 138 138 138 255 1 0 139 139 139 255 1 0 140 140 140 255 1 0 141 141 141 255 1 0 142 142 142 255 1 0 143 143 143 255 1 0 144 144 144 255 1 0 145 145 145 255 1 0 146 146 146 255 1 0 147 147 147 255 1 0 147 147 147 255 1 0 149 149 149 255 1 0 150 150 150 255 1 0 151 151 151 255 1 0 152 152 152 255 1 0 153 153 153 255 1 0 154 154 154 255 1 0 155 155 155 255 1 0 156 156 156 255 1 0 157 157 157 255 1 0 158 158 158 255 1 0 159 159 159 255 1 0 160 160 160 255 1 0 161 161 161 255 1 0 162 162 162 255 1 0 163 163 163 255 1 0 163 163 163 255 1 0 165 165 165 255 1 0 166 166 166 255 1 0 167 167 167 255 1 0 168 168 168 255 1 0 169 169 169 255 1 0 170 170 170 255 1 0 171 171 171 255 1 0 172 172 172 255 1 0 173 173 173 255 1 0 174 174 174 255 1 0 175 175 175 255 1 0 176 176 176 255 1 0 177 177 177 255 1 0 178 178 178 255 1 0 179 179 179 255 1 0 179 179 179 255 1 0 181 181 181 255 1 0 182 182 182 255 1 0 183 183 183 255 1 0 184 184 184 255 1 0 185 185 185 255 1 0 186 186 186 255 1 0 187 187 187 255 1 0 188 188 188 255 1 0 189 189 189 255 1 0 190 190 190 255 1 0 191 191 191 255 1 0 192 192 192 255 1 0 193 193 193 255 1 0 194 194 194 255 1 0 195 195 195 255 1 0 195 195 195 255 1 0 197 197 197 255 1 0 198 198 198 255 1 0 199 199 199 255 1 0 200 200 200 255 1 0 201 201 201 255 1 0 202 202 202 255 1 0 203 203 203 255 1 0 204 204 204 255 1 0 205 205 205 255 1 0 206 206 206 255 1 0 207 207 207 255 1 0 208 208 208 255 1 0 209 209 209 255 1 0 210 210 210 255 1 0 211 211 211 255 1 0 211 211 211 255 1 0 213 213 213 255 1 0 214 214 214 255 1 0 215 215 215 255 1 0 216 216 216 255 1 0 217 217 217 255 1 0 218 218 218 255 1 0 219 219 219 255 1 0 220 220 220 255 1 0 221 221 221 255 1 0 222 222 222 255 1 0 223 223 223 255 1 0 224 224 224 255 1 0 225 225 225 255 1 0 226 226 226 255 1 0 227 227 227 255 1 0 228 228 228 255 1 0 229 229 229 255 1 0 230 230 230 255 1 0 231 231 231 255 1 0 232 232 232 255 1 0 233 233 233 255 1 0 234 234 234 255 1 0 235 235 235 255 1 0 236 236 236 255 1 0 237 237 237 255 1 0 238 238 238 255 1 0 239 239 239 255 1 0 240 240 240 255 1 0 241 241 241 255 1 0 242 242 242 255 1 0 243 243 243 255 1 0 244 244 244 255 1 0 245 245 245 255 1 0 246 246 246 255 1 0 247 247 247 255 1 0 248 248 248 255 1 0 249 249 249 255 1 0 250 250 250 255 1 0 251 251 251 255 1 0 252 252 252 255 1 0 253 253 253 255 1 0 254 254 254 255 1 0 255 255 255 255 1 diff --git a/python/tests/reference/Colormap/binary.msh b/python/tests/reference/Colormap/binary.msh new file mode 100644 index 000000000..66b1cf863 --- /dev/null +++ b/python/tests/reference/Colormap/binary.msh @@ -0,0 +1,258 @@ +View.ColorTable = { +255.0,255.0,255.0, +254.0,254.0,254.0, +253.0,253.0,253.0, +252.0,252.0,252.0, +251.0,251.0,251.0, +250.0,250.0,250.0, +249.0,249.0,249.0, +248.0,248.0,248.0, +247.0,247.0,247.0, +246.0,246.0,246.0, +245.0,245.0,245.0, +244.0,244.0,244.0, +243.0,243.0,243.0, +242.0,242.0,242.0, +241.0,241.0,241.0, +240.0,240.0,240.0, +239.0,239.0,239.0, +238.0,238.0,238.0, +237.0,237.0,237.0, +236.0,236.0,236.0, +235.00000000000003,235.00000000000003,235.00000000000003, +234.0,234.0,234.0, +233.0,233.0,233.0, +232.0,232.0,232.0, +231.0,231.0,231.0, +230.0,230.0,230.0, +229.0,229.0,229.0, +228.0,228.0,228.0, +227.0,227.0,227.0, +226.0,226.0,226.0, +225.0,225.0,225.0, +224.0,224.0,224.0, +223.0,223.0,223.0, +222.0,222.0,222.0, +221.0,221.0,221.0, +220.0,220.0,220.0, +219.00000000000003,219.00000000000003,219.00000000000003, +218.00000000000003,218.00000000000003,218.00000000000003, +217.0,217.0,217.0, +216.0,216.0,216.0, +215.0,215.0,215.0, +214.0,214.0,214.0, +213.0,213.0,213.0, +211.99999999999997,211.99999999999997,211.99999999999997, +211.0,211.0,211.0, +210.0,210.0,210.0, +209.0,209.0,209.0, +208.0,208.0,208.0, +207.0,207.0,207.0, +206.0,206.0,206.0, +205.0,205.0,205.0, +204.0,204.0,204.0, +203.00000000000003,203.00000000000003,203.00000000000003, +202.00000000000003,202.00000000000003,202.00000000000003, +201.0,201.0,201.0, +200.0,200.0,200.0, +199.0,199.0,199.0, +198.0,198.0,198.0, +197.0,197.0,197.0, +195.99999999999997,195.99999999999997,195.99999999999997, +195.0,195.0,195.0, +194.0,194.0,194.0, +193.0,193.0,193.0, +192.0,192.0,192.0, +191.0,191.0,191.0, +190.0,190.0,190.0, +189.0,189.0,189.0, +188.0,188.0,188.0, +187.00000000000003,187.00000000000003,187.00000000000003, +186.00000000000003,186.00000000000003,186.00000000000003, +185.0,185.0,185.0, +184.0,184.0,184.0, +183.0,183.0,183.0, +182.0,182.0,182.0, +181.0,181.0,181.0, +179.99999999999997,179.99999999999997,179.99999999999997, +179.0,179.0,179.0, +178.0,178.0,178.0, +177.0,177.0,177.0, +176.0,176.0,176.0, +175.0,175.0,175.0, +174.0,174.0,174.0, +173.0,173.0,173.0, +172.0,172.0,172.0, +171.00000000000003,171.00000000000003,171.00000000000003, +170.00000000000003,170.00000000000003,170.00000000000003, +169.0,169.0,169.0, +168.0,168.0,168.0, +167.0,167.0,167.0, +166.0,166.0,166.0, +165.0,165.0,165.0, +163.99999999999997,163.99999999999997,163.99999999999997, +163.0,163.0,163.0, +162.0,162.0,162.0, +161.0,161.0,161.0, +160.0,160.0,160.0, +159.0,159.0,159.0, +158.0,158.0,158.0, +157.0,157.0,157.0, +156.0,156.0,156.0, +155.00000000000003,155.00000000000003,155.00000000000003, +154.00000000000003,154.00000000000003,154.00000000000003, +153.0,153.0,153.0, +152.0,152.0,152.0, +151.0,151.0,151.0, +150.0,150.0,150.0, +149.0,149.0,149.0, +147.99999999999997,147.99999999999997,147.99999999999997, +147.0,147.0,147.0, +146.0,146.0,146.0, +145.0,145.0,145.0, +144.0,144.0,144.0, +143.0,143.0,143.0, +142.0,142.0,142.0, +141.0,141.0,141.0, +140.0,140.0,140.0, +139.00000000000003,139.00000000000003,139.00000000000003, +138.00000000000003,138.00000000000003,138.00000000000003, +137.0,137.0,137.0, +136.0,136.0,136.0, +135.0,135.0,135.0, +134.0,134.0,134.0, +133.0,133.0,133.0, +131.99999999999997,131.99999999999997,131.99999999999997, +131.0,131.0,131.0, +130.0,130.0,130.0, +129.0,129.0,129.0, +128.0,128.0,128.0, +127.0,127.0,127.0, +126.0,126.0,126.0, +125.00000000000001,125.00000000000001,125.00000000000001, +124.00000000000001,124.00000000000001,124.00000000000001, +123.00000000000001,123.00000000000001,123.00000000000001, +121.99999999999999,121.99999999999999,121.99999999999999, +121.0,121.0,121.0, +120.0,120.0,120.0, +119.0,119.0,119.0, +118.0,118.0,118.0, +117.00000000000001,117.00000000000001,117.00000000000001, +116.00000000000001,116.00000000000001,116.00000000000001, +114.99999999999999,114.99999999999999,114.99999999999999, +113.99999999999999,113.99999999999999,113.99999999999999, +113.0,113.0,113.0, +112.0,112.0,112.0, +111.0,111.0,111.0, +110.0,110.0,110.0, +109.00000000000001,109.00000000000001,109.00000000000001, +108.00000000000001,108.00000000000001,108.00000000000001, +107.00000000000001,107.00000000000001,107.00000000000001, +105.99999999999999,105.99999999999999,105.99999999999999, +105.0,105.0,105.0, +104.0,104.0,104.0, +103.0,103.0,103.0, +102.0,102.0,102.0, +101.00000000000001,101.00000000000001,101.00000000000001, +100.00000000000001,100.00000000000001,100.00000000000001, +98.99999999999999,98.99999999999999,98.99999999999999, +97.99999999999999,97.99999999999999,97.99999999999999, +97.0,97.0,97.0, +96.0,96.0,96.0, +95.0,95.0,95.0, +94.0,94.0,94.0, +93.00000000000001,93.00000000000001,93.00000000000001, +92.00000000000001,92.00000000000001,92.00000000000001, +91.00000000000001,91.00000000000001,91.00000000000001, +89.99999999999999,89.99999999999999,89.99999999999999, +89.0,89.0,89.0, +88.0,88.0,88.0, +87.0,87.0,87.0, +86.0,86.0,86.0, +85.00000000000001,85.00000000000001,85.00000000000001, +84.00000000000001,84.00000000000001,84.00000000000001, +82.99999999999999,82.99999999999999,82.99999999999999, +81.99999999999999,81.99999999999999,81.99999999999999, +81.0,81.0,81.0, +80.0,80.0,80.0, +79.0,79.0,79.0, +78.0,78.0,78.0, +77.00000000000001,77.00000000000001,77.00000000000001, +76.00000000000001,76.00000000000001,76.00000000000001, +75.00000000000001,75.00000000000001,75.00000000000001, +73.99999999999999,73.99999999999999,73.99999999999999, +73.0,73.0,73.0, +72.0,72.0,72.0, +71.0,71.0,71.0, +70.0,70.0,70.0, +69.00000000000001,69.00000000000001,69.00000000000001, +68.00000000000001,68.00000000000001,68.00000000000001, +66.99999999999999,66.99999999999999,66.99999999999999, +65.99999999999999,65.99999999999999,65.99999999999999, +65.0,65.0,65.0, +64.0,64.0,64.0, +63.0,63.0,63.0, +62.00000000000001,62.00000000000001,62.00000000000001, +61.00000000000001,61.00000000000001,61.00000000000001, +60.000000000000014,60.000000000000014,60.000000000000014, +59.000000000000014,59.000000000000014,59.000000000000014, +57.99999999999999,57.99999999999999,57.99999999999999, +56.99999999999999,56.99999999999999,56.99999999999999, +56.0,56.0,56.0, +55.0,55.0,55.0, +54.00000000000001,54.00000000000001,54.00000000000001, +53.00000000000001,53.00000000000001,53.00000000000001, +52.000000000000014,52.000000000000014,52.000000000000014, +50.999999999999986,50.999999999999986,50.999999999999986, +49.99999999999999,49.99999999999999,49.99999999999999, +48.99999999999999,48.99999999999999,48.99999999999999, +48.0,48.0,48.0, +47.0,47.0,47.0, +46.00000000000001,46.00000000000001,46.00000000000001, +45.00000000000001,45.00000000000001,45.00000000000001, +44.000000000000014,44.000000000000014,44.000000000000014, +43.000000000000014,43.000000000000014,43.000000000000014, +41.99999999999999,41.99999999999999,41.99999999999999, +40.99999999999999,40.99999999999999,40.99999999999999, +40.0,40.0,40.0, +39.0,39.0,39.0, +38.00000000000001,38.00000000000001,38.00000000000001, +37.00000000000001,37.00000000000001,37.00000000000001, +36.000000000000014,36.000000000000014,36.000000000000014, +34.999999999999986,34.999999999999986,34.999999999999986, +33.99999999999999,33.99999999999999,33.99999999999999, +32.99999999999999,32.99999999999999,32.99999999999999, +32.0,32.0,32.0, +31.000000000000004,31.000000000000004,31.000000000000004, +30.000000000000007,30.000000000000007,30.000000000000007, +29.00000000000001,29.00000000000001,29.00000000000001, +28.000000000000014,28.000000000000014,28.000000000000014, +27.000000000000018,27.000000000000018,27.000000000000018, +25.999999999999993,25.999999999999993,25.999999999999993, +24.999999999999996,24.999999999999996,24.999999999999996, +24.0,24.0,24.0, +23.000000000000004,23.000000000000004,23.000000000000004, +22.000000000000007,22.000000000000007,22.000000000000007, +21.00000000000001,21.00000000000001,21.00000000000001, +20.000000000000014,20.000000000000014,20.000000000000014, +18.99999999999999,18.99999999999999,18.99999999999999, +17.999999999999993,17.999999999999993,17.999999999999993, +16.999999999999996,16.999999999999996,16.999999999999996, +16.0,16.0,16.0, +15.000000000000004,15.000000000000004,15.000000000000004, +14.000000000000007,14.000000000000007,14.000000000000007, +13.00000000000001,13.00000000000001,13.00000000000001, +12.000000000000014,12.000000000000014,12.000000000000014, +11.000000000000018,11.000000000000018,11.000000000000018, +9.999999999999993,9.999999999999993,9.999999999999993, +8.999999999999996,8.999999999999996,8.999999999999996, +8.0,8.0,8.0, +7.0000000000000036,7.0000000000000036,7.0000000000000036, +6.000000000000007,6.000000000000007,6.000000000000007, +5.000000000000011,5.000000000000011,5.000000000000011, +4.000000000000014,4.000000000000014,4.000000000000014, +2.9999999999999893,2.9999999999999893,2.9999999999999893, +1.999999999999993,1.999999999999993,1.999999999999993, +0.9999999999999964,0.9999999999999964,0.9999999999999964, +0.0,0.0,0.0, +} diff --git a/python/tests/reference/Colormap/binary.txt b/python/tests/reference/Colormap/binary.txt new file mode 100644 index 000000000..976c0202a --- /dev/null +++ b/python/tests/reference/Colormap/binary.txt @@ -0,0 +1,258 @@ +# Creator: damask.Colormap v99.99.99-9999-pytest +1_RGBA 2_RGBA 3_RGBA 4_RGBA +1.0 1.0 1.0 1.0 +0.996078431372549 0.996078431372549 0.996078431372549 1.0 +0.9921568627450981 0.9921568627450981 0.9921568627450981 1.0 +0.9882352941176471 0.9882352941176471 0.9882352941176471 1.0 +0.9843137254901961 0.9843137254901961 0.9843137254901961 1.0 +0.9803921568627451 0.9803921568627451 0.9803921568627451 1.0 +0.9764705882352941 0.9764705882352941 0.9764705882352941 1.0 +0.9725490196078431 0.9725490196078431 0.9725490196078431 1.0 +0.9686274509803922 0.9686274509803922 0.9686274509803922 1.0 +0.9647058823529412 0.9647058823529412 0.9647058823529412 1.0 +0.9607843137254902 0.9607843137254902 0.9607843137254902 1.0 +0.9568627450980393 0.9568627450980393 0.9568627450980393 1.0 +0.9529411764705882 0.9529411764705882 0.9529411764705882 1.0 +0.9490196078431372 0.9490196078431372 0.9490196078431372 1.0 +0.9450980392156862 0.9450980392156862 0.9450980392156862 1.0 +0.9411764705882353 0.9411764705882353 0.9411764705882353 1.0 +0.9372549019607843 0.9372549019607843 0.9372549019607843 1.0 +0.9333333333333333 0.9333333333333333 0.9333333333333333 1.0 +0.9294117647058824 0.9294117647058824 0.9294117647058824 1.0 +0.9254901960784314 0.9254901960784314 0.9254901960784314 1.0 +0.9215686274509804 0.9215686274509804 0.9215686274509804 1.0 +0.9176470588235294 0.9176470588235294 0.9176470588235294 1.0 +0.9137254901960784 0.9137254901960784 0.9137254901960784 1.0 +0.9098039215686274 0.9098039215686274 0.9098039215686274 1.0 +0.9058823529411765 0.9058823529411765 0.9058823529411765 1.0 +0.9019607843137255 0.9019607843137255 0.9019607843137255 1.0 +0.8980392156862745 0.8980392156862745 0.8980392156862745 1.0 +0.8941176470588236 0.8941176470588236 0.8941176470588236 1.0 +0.8901960784313725 0.8901960784313725 0.8901960784313725 1.0 +0.8862745098039215 0.8862745098039215 0.8862745098039215 1.0 +0.8823529411764706 0.8823529411764706 0.8823529411764706 1.0 +0.8784313725490196 0.8784313725490196 0.8784313725490196 1.0 +0.8745098039215686 0.8745098039215686 0.8745098039215686 1.0 +0.8705882352941177 0.8705882352941177 0.8705882352941177 1.0 +0.8666666666666667 0.8666666666666667 0.8666666666666667 1.0 +0.8627450980392157 0.8627450980392157 0.8627450980392157 1.0 +0.8588235294117648 0.8588235294117648 0.8588235294117648 1.0 +0.8549019607843138 0.8549019607843138 0.8549019607843138 1.0 +0.8509803921568627 0.8509803921568627 0.8509803921568627 1.0 +0.8470588235294118 0.8470588235294118 0.8470588235294118 1.0 +0.8431372549019608 0.8431372549019608 0.8431372549019608 1.0 +0.8392156862745098 0.8392156862745098 0.8392156862745098 1.0 +0.8352941176470589 0.8352941176470589 0.8352941176470589 1.0 +0.8313725490196078 0.8313725490196078 0.8313725490196078 1.0 +0.8274509803921568 0.8274509803921568 0.8274509803921568 1.0 +0.8235294117647058 0.8235294117647058 0.8235294117647058 1.0 +0.8196078431372549 0.8196078431372549 0.8196078431372549 1.0 +0.8156862745098039 0.8156862745098039 0.8156862745098039 1.0 +0.8117647058823529 0.8117647058823529 0.8117647058823529 1.0 +0.807843137254902 0.807843137254902 0.807843137254902 1.0 +0.803921568627451 0.803921568627451 0.803921568627451 1.0 +0.8 0.8 0.8 1.0 +0.7960784313725491 0.7960784313725491 0.7960784313725491 1.0 +0.7921568627450981 0.7921568627450981 0.7921568627450981 1.0 +0.788235294117647 0.788235294117647 0.788235294117647 1.0 +0.7843137254901961 0.7843137254901961 0.7843137254901961 1.0 +0.7803921568627451 0.7803921568627451 0.7803921568627451 1.0 +0.7764705882352941 0.7764705882352941 0.7764705882352941 1.0 +0.7725490196078432 0.7725490196078432 0.7725490196078432 1.0 +0.7686274509803921 0.7686274509803921 0.7686274509803921 1.0 +0.7647058823529411 0.7647058823529411 0.7647058823529411 1.0 +0.7607843137254902 0.7607843137254902 0.7607843137254902 1.0 +0.7568627450980392 0.7568627450980392 0.7568627450980392 1.0 +0.7529411764705882 0.7529411764705882 0.7529411764705882 1.0 +0.7490196078431373 0.7490196078431373 0.7490196078431373 1.0 +0.7450980392156863 0.7450980392156863 0.7450980392156863 1.0 +0.7411764705882353 0.7411764705882353 0.7411764705882353 1.0 +0.7372549019607844 0.7372549019607844 0.7372549019607844 1.0 +0.7333333333333334 0.7333333333333334 0.7333333333333334 1.0 +0.7294117647058824 0.7294117647058824 0.7294117647058824 1.0 +0.7254901960784313 0.7254901960784313 0.7254901960784313 1.0 +0.7215686274509804 0.7215686274509804 0.7215686274509804 1.0 +0.7176470588235294 0.7176470588235294 0.7176470588235294 1.0 +0.7137254901960784 0.7137254901960784 0.7137254901960784 1.0 +0.7098039215686275 0.7098039215686275 0.7098039215686275 1.0 +0.7058823529411764 0.7058823529411764 0.7058823529411764 1.0 +0.7019607843137254 0.7019607843137254 0.7019607843137254 1.0 +0.6980392156862745 0.6980392156862745 0.6980392156862745 1.0 +0.6941176470588235 0.6941176470588235 0.6941176470588235 1.0 +0.6901960784313725 0.6901960784313725 0.6901960784313725 1.0 +0.6862745098039216 0.6862745098039216 0.6862745098039216 1.0 +0.6823529411764706 0.6823529411764706 0.6823529411764706 1.0 +0.6784313725490196 0.6784313725490196 0.6784313725490196 1.0 +0.6745098039215687 0.6745098039215687 0.6745098039215687 1.0 +0.6705882352941177 0.6705882352941177 0.6705882352941177 1.0 +0.6666666666666667 0.6666666666666667 0.6666666666666667 1.0 +0.6627450980392157 0.6627450980392157 0.6627450980392157 1.0 +0.6588235294117647 0.6588235294117647 0.6588235294117647 1.0 +0.6549019607843137 0.6549019607843137 0.6549019607843137 1.0 +0.6509803921568628 0.6509803921568628 0.6509803921568628 1.0 +0.6470588235294118 0.6470588235294118 0.6470588235294118 1.0 +0.6431372549019607 0.6431372549019607 0.6431372549019607 1.0 +0.6392156862745098 0.6392156862745098 0.6392156862745098 1.0 +0.6352941176470588 0.6352941176470588 0.6352941176470588 1.0 +0.6313725490196078 0.6313725490196078 0.6313725490196078 1.0 +0.6274509803921569 0.6274509803921569 0.6274509803921569 1.0 +0.6235294117647059 0.6235294117647059 0.6235294117647059 1.0 +0.6196078431372549 0.6196078431372549 0.6196078431372549 1.0 +0.615686274509804 0.615686274509804 0.615686274509804 1.0 +0.611764705882353 0.611764705882353 0.611764705882353 1.0 +0.607843137254902 0.607843137254902 0.607843137254902 1.0 +0.603921568627451 0.603921568627451 0.603921568627451 1.0 +0.6 0.6 0.6 1.0 +0.596078431372549 0.596078431372549 0.596078431372549 1.0 +0.592156862745098 0.592156862745098 0.592156862745098 1.0 +0.5882352941176471 0.5882352941176471 0.5882352941176471 1.0 +0.5843137254901961 0.5843137254901961 0.5843137254901961 1.0 +0.580392156862745 0.580392156862745 0.580392156862745 1.0 +0.5764705882352941 0.5764705882352941 0.5764705882352941 1.0 +0.5725490196078431 0.5725490196078431 0.5725490196078431 1.0 +0.5686274509803921 0.5686274509803921 0.5686274509803921 1.0 +0.5647058823529412 0.5647058823529412 0.5647058823529412 1.0 +0.5607843137254902 0.5607843137254902 0.5607843137254902 1.0 +0.5568627450980392 0.5568627450980392 0.5568627450980392 1.0 +0.5529411764705883 0.5529411764705883 0.5529411764705883 1.0 +0.5490196078431373 0.5490196078431373 0.5490196078431373 1.0 +0.5450980392156863 0.5450980392156863 0.5450980392156863 1.0 +0.5411764705882354 0.5411764705882354 0.5411764705882354 1.0 +0.5372549019607843 0.5372549019607843 0.5372549019607843 1.0 +0.5333333333333333 0.5333333333333333 0.5333333333333333 1.0 +0.5294117647058824 0.5294117647058824 0.5294117647058824 1.0 +0.5254901960784314 0.5254901960784314 0.5254901960784314 1.0 +0.5215686274509804 0.5215686274509804 0.5215686274509804 1.0 +0.5176470588235293 0.5176470588235293 0.5176470588235293 1.0 +0.5137254901960784 0.5137254901960784 0.5137254901960784 1.0 +0.5098039215686274 0.5098039215686274 0.5098039215686274 1.0 +0.5058823529411764 0.5058823529411764 0.5058823529411764 1.0 +0.5019607843137255 0.5019607843137255 0.5019607843137255 1.0 +0.4980392156862745 0.4980392156862745 0.4980392156862745 1.0 +0.49411764705882355 0.49411764705882355 0.49411764705882355 1.0 +0.4901960784313726 0.4901960784313726 0.4901960784313726 1.0 +0.4862745098039216 0.4862745098039216 0.4862745098039216 1.0 +0.48235294117647065 0.48235294117647065 0.48235294117647065 1.0 +0.4784313725490196 0.4784313725490196 0.4784313725490196 1.0 +0.4745098039215686 0.4745098039215686 0.4745098039215686 1.0 +0.47058823529411764 0.47058823529411764 0.47058823529411764 1.0 +0.4666666666666667 0.4666666666666667 0.4666666666666667 1.0 +0.4627450980392157 0.4627450980392157 0.4627450980392157 1.0 +0.45882352941176474 0.45882352941176474 0.45882352941176474 1.0 +0.4549019607843138 0.4549019607843138 0.4549019607843138 1.0 +0.4509803921568627 0.4509803921568627 0.4509803921568627 1.0 +0.44705882352941173 0.44705882352941173 0.44705882352941173 1.0 +0.44313725490196076 0.44313725490196076 0.44313725490196076 1.0 +0.4392156862745098 0.4392156862745098 0.4392156862745098 1.0 +0.43529411764705883 0.43529411764705883 0.43529411764705883 1.0 +0.43137254901960786 0.43137254901960786 0.43137254901960786 1.0 +0.4274509803921569 0.4274509803921569 0.4274509803921569 1.0 +0.42352941176470593 0.42352941176470593 0.42352941176470593 1.0 +0.41960784313725497 0.41960784313725497 0.41960784313725497 1.0 +0.4156862745098039 0.4156862745098039 0.4156862745098039 1.0 +0.4117647058823529 0.4117647058823529 0.4117647058823529 1.0 +0.40784313725490196 0.40784313725490196 0.40784313725490196 1.0 +0.403921568627451 0.403921568627451 0.403921568627451 1.0 +0.4 0.4 0.4 1.0 +0.39607843137254906 0.39607843137254906 0.39607843137254906 1.0 +0.3921568627450981 0.3921568627450981 0.3921568627450981 1.0 +0.388235294117647 0.388235294117647 0.388235294117647 1.0 +0.38431372549019605 0.38431372549019605 0.38431372549019605 1.0 +0.3803921568627451 0.3803921568627451 0.3803921568627451 1.0 +0.3764705882352941 0.3764705882352941 0.3764705882352941 1.0 +0.37254901960784315 0.37254901960784315 0.37254901960784315 1.0 +0.3686274509803922 0.3686274509803922 0.3686274509803922 1.0 +0.3647058823529412 0.3647058823529412 0.3647058823529412 1.0 +0.36078431372549025 0.36078431372549025 0.36078431372549025 1.0 +0.3568627450980393 0.3568627450980393 0.3568627450980393 1.0 +0.3529411764705882 0.3529411764705882 0.3529411764705882 1.0 +0.34901960784313724 0.34901960784313724 0.34901960784313724 1.0 +0.34509803921568627 0.34509803921568627 0.34509803921568627 1.0 +0.3411764705882353 0.3411764705882353 0.3411764705882353 1.0 +0.33725490196078434 0.33725490196078434 0.33725490196078434 1.0 +0.33333333333333337 0.33333333333333337 0.33333333333333337 1.0 +0.3294117647058824 0.3294117647058824 0.3294117647058824 1.0 +0.3254901960784313 0.3254901960784313 0.3254901960784313 1.0 +0.32156862745098036 0.32156862745098036 0.32156862745098036 1.0 +0.3176470588235294 0.3176470588235294 0.3176470588235294 1.0 +0.3137254901960784 0.3137254901960784 0.3137254901960784 1.0 +0.30980392156862746 0.30980392156862746 0.30980392156862746 1.0 +0.3058823529411765 0.3058823529411765 0.3058823529411765 1.0 +0.3019607843137255 0.3019607843137255 0.3019607843137255 1.0 +0.29803921568627456 0.29803921568627456 0.29803921568627456 1.0 +0.2941176470588236 0.2941176470588236 0.2941176470588236 1.0 +0.2901960784313725 0.2901960784313725 0.2901960784313725 1.0 +0.28627450980392155 0.28627450980392155 0.28627450980392155 1.0 +0.2823529411764706 0.2823529411764706 0.2823529411764706 1.0 +0.2784313725490196 0.2784313725490196 0.2784313725490196 1.0 +0.27450980392156865 0.27450980392156865 0.27450980392156865 1.0 +0.2705882352941177 0.2705882352941177 0.2705882352941177 1.0 +0.2666666666666667 0.2666666666666667 0.2666666666666667 1.0 +0.26274509803921564 0.26274509803921564 0.26274509803921564 1.0 +0.2588235294117647 0.2588235294117647 0.2588235294117647 1.0 +0.2549019607843137 0.2549019607843137 0.2549019607843137 1.0 +0.25098039215686274 0.25098039215686274 0.25098039215686274 1.0 +0.24705882352941178 0.24705882352941178 0.24705882352941178 1.0 +0.2431372549019608 0.2431372549019608 0.2431372549019608 1.0 +0.23921568627450984 0.23921568627450984 0.23921568627450984 1.0 +0.23529411764705888 0.23529411764705888 0.23529411764705888 1.0 +0.2313725490196079 0.2313725490196079 0.2313725490196079 1.0 +0.22745098039215683 0.22745098039215683 0.22745098039215683 1.0 +0.22352941176470587 0.22352941176470587 0.22352941176470587 1.0 +0.2196078431372549 0.2196078431372549 0.2196078431372549 1.0 +0.21568627450980393 0.21568627450980393 0.21568627450980393 1.0 +0.21176470588235297 0.21176470588235297 0.21176470588235297 1.0 +0.207843137254902 0.207843137254902 0.207843137254902 1.0 +0.20392156862745103 0.20392156862745103 0.20392156862745103 1.0 +0.19999999999999996 0.19999999999999996 0.19999999999999996 1.0 +0.196078431372549 0.196078431372549 0.196078431372549 1.0 +0.19215686274509802 0.19215686274509802 0.19215686274509802 1.0 +0.18823529411764706 0.18823529411764706 0.18823529411764706 1.0 +0.1843137254901961 0.1843137254901961 0.1843137254901961 1.0 +0.18039215686274512 0.18039215686274512 0.18039215686274512 1.0 +0.17647058823529416 0.17647058823529416 0.17647058823529416 1.0 +0.1725490196078432 0.1725490196078432 0.1725490196078432 1.0 +0.16862745098039222 0.16862745098039222 0.16862745098039222 1.0 +0.16470588235294115 0.16470588235294115 0.16470588235294115 1.0 +0.16078431372549018 0.16078431372549018 0.16078431372549018 1.0 +0.1568627450980392 0.1568627450980392 0.1568627450980392 1.0 +0.15294117647058825 0.15294117647058825 0.15294117647058825 1.0 +0.14901960784313728 0.14901960784313728 0.14901960784313728 1.0 +0.14509803921568631 0.14509803921568631 0.14509803921568631 1.0 +0.14117647058823535 0.14117647058823535 0.14117647058823535 1.0 +0.13725490196078427 0.13725490196078427 0.13725490196078427 1.0 +0.1333333333333333 0.1333333333333333 0.1333333333333333 1.0 +0.12941176470588234 0.12941176470588234 0.12941176470588234 1.0 +0.12549019607843137 0.12549019607843137 0.12549019607843137 1.0 +0.1215686274509804 0.1215686274509804 0.1215686274509804 1.0 +0.11764705882352944 0.11764705882352944 0.11764705882352944 1.0 +0.11372549019607847 0.11372549019607847 0.11372549019607847 1.0 +0.1098039215686275 0.1098039215686275 0.1098039215686275 1.0 +0.10588235294117654 0.10588235294117654 0.10588235294117654 1.0 +0.10196078431372546 0.10196078431372546 0.10196078431372546 1.0 +0.0980392156862745 0.0980392156862745 0.0980392156862745 1.0 +0.09411764705882353 0.09411764705882353 0.09411764705882353 1.0 +0.09019607843137256 0.09019607843137256 0.09019607843137256 1.0 +0.0862745098039216 0.0862745098039216 0.0862745098039216 1.0 +0.08235294117647063 0.08235294117647063 0.08235294117647063 1.0 +0.07843137254901966 0.07843137254901966 0.07843137254901966 1.0 +0.07450980392156858 0.07450980392156858 0.07450980392156858 1.0 +0.07058823529411762 0.07058823529411762 0.07058823529411762 1.0 +0.06666666666666665 0.06666666666666665 0.06666666666666665 1.0 +0.06274509803921569 0.06274509803921569 0.06274509803921569 1.0 +0.05882352941176472 0.05882352941176472 0.05882352941176472 1.0 +0.05490196078431375 0.05490196078431375 0.05490196078431375 1.0 +0.050980392156862786 0.050980392156862786 0.050980392156862786 1.0 +0.04705882352941182 0.04705882352941182 0.04705882352941182 1.0 +0.04313725490196085 0.04313725490196085 0.04313725490196085 1.0 +0.039215686274509776 0.039215686274509776 0.039215686274509776 1.0 +0.03529411764705881 0.03529411764705881 0.03529411764705881 1.0 +0.03137254901960784 0.03137254901960784 0.03137254901960784 1.0 +0.027450980392156876 0.027450980392156876 0.027450980392156876 1.0 +0.02352941176470591 0.02352941176470591 0.02352941176470591 1.0 +0.019607843137254943 0.019607843137254943 0.019607843137254943 1.0 +0.015686274509803977 0.015686274509803977 0.015686274509803977 1.0 +0.0117647058823529 0.0117647058823529 0.0117647058823529 1.0 +0.007843137254901933 0.007843137254901933 0.007843137254901933 1.0 +0.0039215686274509665 0.0039215686274509665 0.0039215686274509665 1.0 +0.0 0.0 0.0 1.0 diff --git a/python/tests/reference/VTK/polyData.vtp b/python/tests/reference/VTK/polyData.vtp index e93e90f58..6ed05f67f 100644 --- a/python/tests/reference/VTK/polyData.vtp +++ b/python/tests/reference/VTK/polyData.vtp @@ -3,11 +3,11 @@ - - AQAAAACAAADwAAAAagAAAA==eF5jYMAGPuyXOV4IRHvsIfQZe8u+xxZ9j1/sh/Eh9B37IjDj4f5QMLhqD6Gf2odB+Pth6iD0G3sFiLn7ofqg+j/CxOH6IfRX+xCouRYQ+6H0D/sCqH6YuRD6D1wd1B9QmsEBxgcAJsNfhw== + + AQAAAACAAAB4AAAAVgAAAA==eF5jYICBhv2WfY9tLfuS7Ypk3PeDaCDf7okF3/7Vq1bZrV6lZQ+k94HEgHL2QHovUM7+iUUfiG0LlQdhkH77Ipnj9iB5qFp7kBjQDiBmcADRANsaLXM= - 0.7453559925 + 0.74535601471 2.4494897428 diff --git a/python/tests/reference/VTK/rectilinearGrid.vtr b/python/tests/reference/VTK/rectilinearGrid.vtr index 3e965bd2e..8d39f6e16 100644 --- a/python/tests/reference/VTK/rectilinearGrid.vtr +++ b/python/tests/reference/VTK/rectilinearGrid.vtr @@ -3,27 +3,27 @@ - - AQAAAACAAACAHwAAywMAAA==eF51mTGKVUEQRX88izByB8byY0MDlyG4CTfgFswMDWQigw8/Ejr40E7woGkYXqLJW4LDb8qmz71VDDjvWMGhijvd0KeTr8dXn/++f/x59rwIf3j6+untw1PS34S/udez8KgP97r+///w8bwIDx/f34SHD3nU4DXxIS/CVx/2N+GrTxWfUV18PC/C132xvwlf9zV51PDck/mQF+HrfNjfhK/z2cXn273+iI/nRXj4+P4mPHzI1zqSfZEX4eu+2N+Er/uanPXl9buXn+9n5n3lRTjzvvY34cx78Pgee7ye6eN5Ec6804ecefc+NfEhL8KZd+9TE58qPqO6+HhehDPv9CFn3v189mQ+5EU48+7ns4sPefhE7ujjeRHOvNOHnHmnz6gj2Rd5Ec6804ecefc+kbtLkvcLfCb3eb/AZ3Kf90uS9+njOfN+SfI+fch93ulTEx9y5p0+7Gfe6VPFZ1QXH8+Zd+6L/cw799XFZ3ju4uM58875sJ9553x28VlzN308Z94vSd6nD7nPO/d1iI/nzDv3xX7mnfs6Ep/Tafvx8eXnl+R95UU48772N+HMe/D4Hnu8nunjeRHOvNOHnHn3PjXxIS/CmXfvUxOfKj6juvh4XoQz7/QhZ979fPZkPuRFOPPu57OLD3n4RO7o43kRzrzTh5x5p8+oI9kXeRHOvNOHnHn3PnHO3iTvK+f5fkvO9xt8Jmfeg8f32GOcs9PHc57vN8k7fciZd+9TEx9ynu/0YT/Pd/pU8RnVxcdznu/cF/t5vnNfXXyG5y4+nvN853zYz/Od89nFZz1np4/nPN9vknf6kDPv9Bl1iI/nPN+5L/bzfOe+jsTndLr/Gdh+S95XXoQz72t/E868B4/vscfrmT6eF+HMO33ImXfvUxMf8iKcefc+NfGp4jOqi4/nRTjzTh9y5t3PZ0/mQ16EM+9+Prv4kIdP5I4+nhfhzDt9yJl3+ow6kn2RF+HMO33ImXfvE/fqTfK+ct7nt+Q+v8FncuY9eHyPPca9evp4zvv8JnmnDznz7n1q4kPO+zx92M/7PH2q+Izq4uM57/PcF/t5n+e+uvgMz118POd9nvNhP+/znM8uPpE7+njO+/wmeacPOfNOn1GH+HjO+zz3xX7e57mvI/GJ6pL3lfM9rkve1/4mnHkPHr+NPca72PTxnO9xXfK+9jfhzLv3qYkPOd/j6MP+Jpx5p8/6zX2R8z2O+2J/E868r//yPY7zIed7HOfD/iaceadP5I4+nvM9rkve6UPOvNNn1CE+nvM9jvtifxPOvAf/B5cwXsg= + + AQAAAACAAADADwAAFQMAAA==eF5Vl7GtFEEQBTeG8wgAhxxux8bEOIsYwDu/M0A6D0JofMAmjU0BF5+d7anXjzP+L6NV4l3pj8S29efL77/35ucO//nwS3zeiL99fTM2+3zPd/u6uTc/d3h67EY8PXB50jxpnjRPmifNk/Kcn4Gn+do18NiNeO3StvPfJk/ztUseuxGvXfI8Hg95mp87PD12I54euD5hu0IeuHbpRly74r9mb9+/7nQvru6T6b5uxHSfPH/PdniaqzseuxHTvT1pnjRPmifNk+ZJec7PsF3Ddg3bxY2Y7rZLnubqbrvkgemOZ7bD01zd8diNmO69K2xXyAPTvXeF7QrzzHa3vbtPpvtt7+7Xjbi73/b1/ex4muleHrsRd3c8aZ40T5onzZPmSXm2q512Dds1bBc34u6uXfI001275IG7e3mqXXma6V4euxF3d3aF7Qp54O7OrrBdYZ5t+/npo7oXV/fJdF83YrpPXt/Pjqe5uuOxGzHd25PmSfOkedI8aZ6U5/wM2zVs17Bd3Ijpbrvkaa7utksemO54Zjs8zdUdj92I6d67wnaFPDDde1fYrjDP9Vare7Heeft7f6n7ZHvn7e/9pe54YHvn1R0PXJ40T5onzZPmSfOkPFu91epuu4bt4kZs77z9vWuXPLC98+puu+RZb7W644HtnVd3PHDNCtsV8sD2zqt77wrzbNvn44e6F1f3yXRfN2K6T17fz46nubrjsRsx3duT5knzpHnSPGmelOf8DNs1bNewXdyI6W675Gmu7rZLHpjueGY7PM3VHY/diOneu8J2hTww3XtX2K4wz3yrD3Uv5p0/7J0/1H1yv/OHuuNp5p0/7J0/1B0PXJ40T5onzZPmSfOkPNv1VmvXsF3DdnEj7ndeu+Rp5p3XLnngfucPdcfTzDt/2Dt/qDseuGaF7Qp54H7n2RW2K8xT3xHdi5/67ui+bsR0nzx/zHZ4mqs7HrsR0709aZ40T5onzZPmSXnWb3YNPDDd8cB0t13yNFd32yUPTHc8sx2eZv0/btAdD0z33hW2K+SB6d67wnYV/wMyiXC6 0 - 1.268857754 + 1.2688577653 - - AQAAAACAAACwEwAAZQIAAA==eF511r2KFEEYRmHjjY2N9Ao0lQ79yQy8jBVjc29gL0Ezhc1maDBfU0FB3NVgNyqQuged1leZ56sqBpo5HRx4+wS13nn989l6vjzfzm45u/vk9+/NcvL17cuHJx8Lf3D/cD4XfvPq9vmj68vCH19vLwpf/3pvbedT8crjlccrj1ce77vtXBavPF55vPJ45fG+3/hN8crjlccrj1d+vHOb7NyKV368cyte+XrUVZ901YtXftxVL175Ss/f96dX+9MPpedwew6353B7DrdnvXJ71iu3Z73pTa/cnvXK7VlvetMrt2e9cnse79wmO7fildvzeOdWvOlt3FUvXrk9j7vqE+9uWQ/46qL0HG7P4fYcbs/h9qxXbs965fasN73plduzXrk9601veuX2rFduz+Od22TnVrxyex7v3Io3vY276sUrt+dxV33i3f37/vYcbs/h9hxuz+H2rFduz3rl9pynPYfbs165PeuV27NeuT3rlduz3j//22TnVrxyew6353B7DrdnvXJ71iu353uHa8jZl9JzuD2H23O4PYfbs165PeuV27Pe9KZXbs965fasN73plduzXrk9j3duk51b8crtebxzK970Nu6qF6/cnsdd9Yl3tzzdLtbfSs/h9hxuz+H2HG7PeuX2rFduz3rTm165PeuV27Pe9KZXbs965fY83rlNdm7FK7fn8c6teNPbuKtevHJ7HnfVJ97d8uJwDdn/KD2H23O4PYfbc7g965Xbs165PetNb3rl9qxXbs9605teuT3rldvzeOc22bkVr9yexzu34k1v46568crtedzVf/4LDINsdg== + + AQAAAACAAADYCQAA8QEAAA==eF5N0CGOFlEQAOHVK0nmBnAFgnn/PLsSsZd4CSQIPDfYZN2iUbMePI4DEMR/BbgDNJ2ve1yZSir18P3jeD6O8eruxfj99s0Ff356Kh63v4o/jNsdP/xzb24+Xbg4XBwuDheHe3//s1wcLg4Xh4vT3fZ2k9NNTjc53eRsnuXibJ7l4mye5T4fq1ycr1a5OF+tk3uMb++u9TnY52Cfg30O9pmLfeZin7nxjYt95mKf2932dpN9bjfZ526e5WKfu3mWi33uV6tc7HO/Wif3GO+vry8+B/sc7HOwz8E+c7HPXOwzN75xsc9c7HO7295uss/tJvvczbNc7HM3z3Kxz/1qlYt97lfr5B7/f/kc7HOwz8E+B/vMxT5zsc/c+MbFPnOxz+1ue7vJPreb7HM3z3Kxz908y8U+96tVLva5X62Te4y7xy/1OdjnYJ+DfQ72mYt95mKfufGNi33mYp/b3fZ2k31uN9nnbp7lYp+7eZaLfe5Xq1zsc79aJ/cYjy9/1Odgn4N9DvY52Gcu9pmLfebGNy72mYt9bnfb2032ud1kn7t5lot97uZZLva5X61ysc/9ap3cY1y//qnPwT4H+xzsc7DPXOwzF/vMjW9c7DMX+9zutreb7HO7yT538ywX+9zNs1zsc79a5WKf+1XyX6K10A4= - 0.10871961483 + 0.10871961652 - 1.1607924027 + 1.1607924233 diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py new file mode 100644 index 000000000..fbe65e2bd --- /dev/null +++ b/python/tests/test_Colormap.py @@ -0,0 +1,151 @@ +import os +import filecmp +import time + +import numpy as np +import pytest + +import damask +from damask import Colormap + +@pytest.fixture +def reference_dir(reference_dir_base): + """Directory containing reference results.""" + return reference_dir_base/'Colormap' + +class TestColormap: + + def test_conversion(self): + + specials = np.array([[0.,0.,0.], + [1.,0.,0.], + [0.,1.,0.], + [0.,0.,1.], + [1.,1.,0.], + [0.,1.,1.], + [1.,0.,1.], + [1.,1.,1.] + ]) + rgbs = np.vstack((specials,np.random.rand(100,3))) + pass # class not integrated + for rgb in rgbs: + print('rgb',rgb) + + # rgb2hsv2rgb + hsv = Colormap._rgb2hsv(rgb) + print('hsv',hsv) + assert np.allclose(Colormap._hsv2rgb(hsv),rgb) + + # rgb2hsl2rgb + hsl = Colormap._rgb2hsl(rgb) + print('hsl',hsl) + assert np.allclose(Colormap._hsl2rgb(hsl),rgb) + + # rgb2xyz2rgb + xyz = Colormap._rgb2xyz(rgb) + print('xyz',xyz) + assert np.allclose(Colormap._xyz2rgb(xyz),rgb,atol=1.e-6,rtol=0) + + # xyz2lab2xyz + lab = Colormap._xyz2lab(xyz) + print('lab',lab) + assert np.allclose(Colormap._lab2xyz(lab),xyz) + + # lab2msh2lab + msh = Colormap._lab2msh(lab) + print('msh',msh) + assert np.allclose(Colormap._msh2lab(msh),lab) + + # lab2rgb2lab + assert np.allclose(Colormap._rgb2lab(Colormap._lab2rgb(lab)),lab,atol=1.e-6,rtol=0) + + # rgb2msh2rgb + assert np.allclose(Colormap._msh2rgb(Colormap._rgb2msh(rgb)),rgb,atol=1.e-6,rtol=0) + + # hsv2msh + assert np.allclose(Colormap._hsv2msh(hsv),msh,atol=1.e-6,rtol=0) + + # hsl2msh + assert np.allclose(Colormap._hsv2msh(hsv),msh,atol=1.e-6,rtol=0) + + # xyz2msh + assert np.allclose(Colormap._xyz2msh(xyz),msh,atol=1.e-6,rtol=0) + + + @pytest.mark.parametrize('format',['ASCII','paraview','GOM','Gmsh']) + @pytest.mark.parametrize('model',['rgb','hsv','hsl','xyz','lab','msh']) + def test_from_range(self,model,format,tmpdir): + N = np.random.randint(2,256) + c = Colormap.from_range(np.random.rand(3),np.random.rand(3),model=model,N=N) + c.to_file(tmpdir/'color_out',format=format) + + @pytest.mark.parametrize('format',['ASCII','paraview','GOM','Gmsh']) + @pytest.mark.parametrize('name',['strain','gnuplot','Greys','PRGn','viridis']) + def test_from_predefined(self,name,format,tmpdir): + N = np.random.randint(2,256) + c = Colormap.from_predefined(name,N) + os.chdir(tmpdir) + c.to_file(format=format) + + @pytest.mark.parametrize('format,name',[('ASCII','test.txt'), + ('paraview','test.json'), + ('GOM','test.legend'), + ('Gmsh','test.msh') + ]) + def test_write_filehandle(self,format,name,tmpdir): + c = Colormap.from_predefined('Dark2') + fname = tmpdir/name + with open(fname,'w') as f: + c.to_file(f,format=format) + for i in range(10): + if fname.exists(): return + time.sleep(.5) + assert False + + def test_write_invalid_format(self): + c = Colormap.from_predefined('Dark2') + with pytest.raises(ValueError): + c.to_file(format='invalid') + + @pytest.mark.parametrize('model',['rgb','hsv','hsl','lab','invalid']) + def test_invalid_color(self,model): + with pytest.raises(ValueError): + c = Colormap.from_range(-2.+np.random.rand(3),np.random.rand(3),N=10,model=model) # noqa + + def test_reversed(self): + c_1 = Colormap.from_predefined('stress') + c_2 = c_1.reversed() + assert (not np.allclose(c_1.colors,c_2.colors)) and \ + np.allclose(c_1.colors,c_2.reversed().colors) + + def test_invert(self): + c_1 = Colormap.from_predefined('strain') + c_2 = ~c_1 + assert (not np.allclose(c_1.colors,c_2.colors)) and \ + np.allclose(c_1.colors,(~c_2).colors) + + def test_add(self): + c = Colormap.from_predefined('jet') + c += c + assert (np.allclose(c.colors[:len(c.colors)//2],c.colors[len(c.colors)//2:])) + + def test_list(self): + Colormap.list_predefined() + + @pytest.mark.parametrize('format,ext',[('ASCII','.txt'), + ('paraview','.json'), + ('GOM','.legend'), + ('Gmsh','.msh') + ]) + def test_compare_reference(self,format,ext,tmpdir,reference_dir,update,monkeypatch): + monkeypatch.setattr(damask, 'version', pytest.dummy_version) + name = 'binary' + c = Colormap.from_predefined(name) + if update: + os.chdir(reference_dir) + c.to_file(format=format) + else: + os.chdir(tmpdir) + c.to_file(format=format) + time.sleep(.5) + assert filecmp.cmp(tmpdir/(name+ext),reference_dir/(name+ext)) diff --git a/python/tests/test_Lattice.py b/python/tests/test_Lattice.py index 01d1aac0c..93fc56445 100644 --- a/python/tests/test_Lattice.py +++ b/python/tests/test_Lattice.py @@ -3,38 +3,154 @@ import random import pytest import numpy as np +from damask import Rotation from damask import Symmetry +def in_FZ(system,rho): + """Non-vectorized version of 'in_FZ'.""" + rho_abs = abs(rho) + + if system == 'cubic': + return np.sqrt(2.0)-1.0 >= rho_abs[0] \ + and np.sqrt(2.0)-1.0 >= rho_abs[1] \ + and np.sqrt(2.0)-1.0 >= rho_abs[2] \ + and 1.0 >= rho_abs[0] + rho_abs[1] + rho_abs[2] + elif system == 'hexagonal': + return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] and 1.0 >= rho_abs[2] \ + and 2.0 >= np.sqrt(3)*rho_abs[0] + rho_abs[1] \ + and 2.0 >= np.sqrt(3)*rho_abs[1] + rho_abs[0] \ + and 2.0 >= np.sqrt(3) + rho_abs[2] + elif system == 'tetragonal': + return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] \ + and np.sqrt(2.0) >= rho_abs[0] + rho_abs[1] \ + and np.sqrt(2.0) >= rho_abs[2] + 1.0 + elif system == 'orthorhombic': + return 1.0 >= rho_abs[0] and 1.0 >= rho_abs[1] and 1.0 >= rho_abs[2] + else: + return np.all(np.isfinite(rho_abs)) + + +def in_disorientation_SST(system,rho): + """Non-vectorized version of 'in_Disorientation_SST'.""" + epsilon = 0.0 + if system == 'cubic': + return rho[0] >= rho[1]+epsilon and rho[1] >= rho[2]+epsilon and rho[2] >= epsilon + elif system == 'hexagonal': + return rho[0] >= np.sqrt(3)*(rho[1]-epsilon) and rho[1] >= epsilon and rho[2] >= epsilon + elif system == 'tetragonal': + return rho[0] >= rho[1]-epsilon and rho[1] >= epsilon and rho[2] >= epsilon + elif system == 'orthorhombic': + return rho[0] >= epsilon and rho[1] >= epsilon and rho[2] >= epsilon + else: + return True + + +def in_SST(system,vector,proper = False): + """Non-vectorized version of 'in_SST'.""" + if system == 'cubic': + basis = {'improper':np.array([ [-1. , 0. , 1. ], + [ np.sqrt(2.) , -np.sqrt(2.) , 0. ], + [ 0. , np.sqrt(3.) , 0. ] ]), + 'proper':np.array([ [ 0. , -1. , 1. ], + [-np.sqrt(2.) , np.sqrt(2.) , 0. ], + [ np.sqrt(3.) , 0. , 0. ] ]), + } + elif system == 'hexagonal': + basis = {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -np.sqrt(3.) , 0. ], + [ 0. , 2. , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , np.sqrt(3.) , 0. ], + [ np.sqrt(3.) , -1. , 0. ] ]), + } + elif system == 'tetragonal': + basis = {'improper':np.array([ [ 0. , 0. , 1. ], + [ 1. , -1. , 0. ], + [ 0. , np.sqrt(2.) , 0. ] ]), + 'proper':np.array([ [ 0. , 0. , 1. ], + [-1. , 1. , 0. ], + [ np.sqrt(2.) , 0. , 0. ] ]), + } + elif system == 'orthorhombic': + basis = {'improper':np.array([ [ 0., 0., 1.], + [ 1., 0., 0.], + [ 0., 1., 0.] ]), + 'proper':np.array([ [ 0., 0., 1.], + [-1., 0., 0.], + [ 0., 1., 0.] ]), + } + else: + return True + + v = np.array(vector,dtype=float) + if proper: + theComponents = np.around(np.dot(basis['improper'],v),12) + inSST = np.all(theComponents >= 0.0) + if not inSST: + theComponents = np.around(np.dot(basis['proper'],v),12) + inSST = np.all(theComponents >= 0.0) + else: + v[2] = abs(v[2]) + theComponents = np.around(np.dot(basis['improper'],v),12) + inSST = np.all(theComponents >= 0.0) + + return inSST + + +@pytest.fixture +def set_of_rodrigues(set_of_quaternions): + return Rotation(set_of_quaternions).as_Rodrigues(vector=True)[:200] + class TestSymmetry: + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_in_FZ_vectorize(self,set_of_rodrigues,system): + result = Symmetry(system).in_FZ(set_of_rodrigues.reshape(50,4,3)).reshape(200) + for i,r in enumerate(result): + assert r == in_FZ(system,set_of_rodrigues[i]) + + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_in_disorientation_SST_vectorize(self,set_of_rodrigues,system): + result = Symmetry(system).in_disorientation_SST(set_of_rodrigues.reshape(50,4,3)).reshape(200) + for i,r in enumerate(result): + assert r == in_disorientation_SST(system,set_of_rodrigues[i]) + + @pytest.mark.parametrize('proper',[True,False]) + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_in_SST_vectorize(self,system,proper): + vecs = np.random.rand(20,4,3) + result = Symmetry(system).in_SST(vecs,proper).reshape(20*4) + for i,r in enumerate(result): + assert r == in_SST(system,vecs.reshape(20*4,3)[i],proper) + @pytest.mark.parametrize('invalid_symmetry',['fcc','bcc','hello']) def test_invalid_symmetry(self,invalid_symmetry): with pytest.raises(KeyError): s = Symmetry(invalid_symmetry) # noqa def test_equal(self): - symmetry = random.choice(Symmetry.lattices) + symmetry = random.choice(Symmetry.crystal_systems) print(symmetry) assert Symmetry(symmetry) == Symmetry(symmetry) def test_not_equal(self): - symmetries = random.sample(Symmetry.lattices,k=2) + symmetries = random.sample(Symmetry.crystal_systems,k=2) assert Symmetry(symmetries[0]) != Symmetry(symmetries[1]) - @pytest.mark.parametrize('lattice',Symmetry.lattices) - def test_inFZ(self,lattice): - assert Symmetry(lattice).inFZ(np.zeros(3)) + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_in_FZ(self,system): + assert Symmetry(system).in_FZ(np.zeros(3)) - @pytest.mark.parametrize('lattice',Symmetry.lattices) - def test_inDisorientationSST(self,lattice): - assert Symmetry(lattice).inDisorientationSST(np.zeros(3)) + @pytest.mark.parametrize('system',Symmetry.crystal_systems) + def test_in_disorientation_SST(self,system): + assert Symmetry(system).in_disorientation_SST(np.zeros(3)) - @pytest.mark.parametrize('lattice',Symmetry.lattices) + @pytest.mark.parametrize('system',Symmetry.crystal_systems) @pytest.mark.parametrize('proper',[True,False]) - def test_inSST(self,lattice,proper): - assert Symmetry(lattice).inSST(np.zeros(3),proper) + def test_in_SST(self,system,proper): + assert Symmetry(system).in_SST(np.zeros(3),proper) - @pytest.mark.parametrize('function',['inFZ','inDisorientationSST']) + @pytest.mark.parametrize('function',['in_FZ','in_disorientation_SST','in_SST']) def test_invalid_argument(self,function): s = Symmetry() # noqa with pytest.raises(ValueError): diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index e811b2043..84414a343 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -11,6 +11,25 @@ from damask import Lattice n = 1000 +def IPF_color(orientation,direction): + """TSL color of inverse pole figure for given axis (non-vectorized).""" + for o in orientation.equivalent: + pole = o.rotation@direction + inSST,color = orientation.lattice.in_SST(pole,color=True) + if inSST: break + + return color + +def inverse_pole(orientation,axis,proper=False,SST=True): + if SST: + for eq in orientation.equivalent: + pole = eq.rotation @ axis/np.linalg.norm(axis) + if orientation.lattice.in_SST(pole,proper=proper): + return pole + else: + return orientation.rotation @ axis/np.linalg.norm(axis) + + @pytest.fixture def reference_dir(reference_dir_base): """Directory containing reference results.""" @@ -19,6 +38,31 @@ def reference_dir(reference_dir_base): class TestOrientation: + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) + @pytest.mark.parametrize('lattice',['fcc','bcc']) + def test_relationship_vectorize(self,set_of_quaternions,lattice,model): + result = Orientation(set_of_quaternions[:200].reshape(50,4,4),lattice).related(model) + ref_qu = result.rotation.quaternion.reshape(-1,200,4) + for i in range(200): + single = Orientation(set_of_quaternions[i],lattice).related(model).rotation.quaternion + assert np.allclose(ref_qu[:,i,:],single) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_IPF_vectorize(self,set_of_quaternions,lattice): + direction = np.random.random(3)*2.0-1 + oris = Orientation(Rotation(set_of_quaternions),lattice)[:200] + for i,color in enumerate(oris.IPF_color(direction)): + assert np.allclose(color,IPF_color(oris[i],direction)) + + @pytest.mark.parametrize('SST',[False,True]) + @pytest.mark.parametrize('proper',[True,False]) + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_inverse_pole_vectorize(self,set_of_quaternions,lattice,SST,proper): + axis = np.random.random(3)*2.0-1 + oris = Orientation(Rotation(set_of_quaternions),lattice)[:200] + for i,pole in enumerate(oris.inverse_pole(axis,SST=SST)): + assert np.allclose(pole,inverse_pole(oris[i],axis,SST=SST)) + @pytest.mark.parametrize('color',[{'label':'red', 'RGB':[1,0,0],'direction':[0,0,1]}, {'label':'green','RGB':[0,1,0],'direction':[0,1,1]}, {'label':'blue', 'RGB':[0,0,1],'direction':[1,1,1]}]) @@ -26,35 +70,63 @@ class TestOrientation: def test_IPF_cubic(self,color,lattice): cube = damask.Orientation(damask.Rotation(),lattice) for direction in set(permutations(np.array(color['direction']))): - assert np.allclose(cube.IPFcolor(np.array(direction)),np.array(color['RGB'])) + assert np.allclose(cube.IPF_color(np.array(direction)),np.array(color['RGB'])) @pytest.mark.parametrize('lattice',Lattice.lattices) - def test_IPF(self,lattice): + def test_IPF_equivalent(self,set_of_quaternions,lattice): direction = np.random.random(3)*2.0-1 - for rot in [Rotation.from_random() for r in range(n//100)]: - R = damask.Orientation(rot,lattice) - color = R.IPFcolor(direction) - for equivalent in R.equivalentOrientations(): - assert np.allclose(color,R.IPFcolor(direction)) + for ori in Orientation(Rotation(set_of_quaternions),lattice)[:200]: + color = ori.IPF_color(direction) + for equivalent in ori.equivalent: + assert np.allclose(color,equivalent.IPF_color(direction)) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_reduced(self,set_of_quaternions,lattice): + oris = Orientation(Rotation(set_of_quaternions),lattice) + reduced = oris.reduced + assert np.all(reduced.in_FZ) and oris.rotation.shape == reduced.rotation.shape + @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['fcc','bcc']) def test_relationship_forward_backward(self,model,lattice): ori = Orientation(Rotation.from_random(),lattice) - for i,r in enumerate(ori.relatedOrientations(model)): - ori2 = r.relatedOrientations(model)[i] + for i,r in enumerate(ori.related(model)): + ori2 = r.related(model)[i] misorientation = ori.rotation.misorientation(ori2.rotation) - assert misorientation.asAxisAngle(degrees=True)[3]<1.0e-5 + assert misorientation.as_axis_angle(degrees=True)[3]<1.0e-5 @pytest.mark.parametrize('model',['Bain','KS','GT','GT_prime','NW','Pitsch']) @pytest.mark.parametrize('lattice',['fcc','bcc']) def test_relationship_reference(self,update,reference_dir,model,lattice): reference = os.path.join(reference_dir,f'{lattice}_{model}.txt') ori = Orientation(Rotation(),lattice) - eu = np.array([o.rotation.as_Eulers(degrees=True) for o in ori.relatedOrientations(model)]) + eu = np.array([o.rotation.as_Eulers(degrees=True) for o in ori.related(model)]) if update: coords = np.array([(1,i+1) for i,x in enumerate(eu)]) table = damask.Table(eu,{'Eulers':(3,)}) table.add('pos',coords) table.to_ASCII(reference) assert np.allclose(eu,damask.Table.from_ASCII(reference).get('Eulers')) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_disorientation360(self,lattice): + R_1 = Orientation(Rotation(),lattice) + R_2 = Orientation(damask.Rotation.from_Eulers([360,0,0],degrees=True),lattice) + assert np.allclose(R_1.disorientation(R_2).as_matrix(),np.eye(3)) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + @pytest.mark.parametrize('angle',[10,20,30,40]) + def test_average(self,angle,lattice): + R_1 = Orientation(Rotation.from_axis_angle([0,0,1,10],degrees=True),lattice) + R_2 = Orientation(Rotation.from_axis_angle([0,0,1,angle],degrees=True),lattice) + avg_angle = R_1.average(R_2).rotation.as_axis_angle(degrees=True,pair=True)[1] + assert np.isclose(avg_angle,10+(angle-10)/2.) + + @pytest.mark.parametrize('lattice',Lattice.lattices) + def test_from_average(self,lattice): + R_1 = Orientation(Rotation.from_random(),lattice) + eqs = [r for r in R_1.equivalent] + R_2 = damask.Orientation.from_average(eqs) + assert np.allclose(R_1.rotation.quaternion,R_2.rotation.quaternion) + diff --git a/python/tests/test_Result.py b/python/tests/test_Result.py index 6940d573d..20c27649b 100644 --- a/python/tests/test_Result.py +++ b/python/tests/test_Result.py @@ -153,16 +153,16 @@ class TestResult: assert np.allclose(in_memory,in_file) @pytest.mark.parametrize('d',[[1,0,0],[0,1,0],[0,0,1]]) - def test_add_IPFcolor(self,default,d): - default.add_IPFcolor('orientation',d) + def test_add_IPF_color(self,default,d): + default.add_IPF_color('orientation',d) loc = {'orientation': default.get_dataset_location('orientation'), 'color': default.get_dataset_location('IPFcolor_[{} {} {}]'.format(*d))} qu = default.read_dataset(loc['orientation']).view(np.double).reshape(-1,4) crystal_structure = default.get_crystal_structure() in_memory = np.empty((qu.shape[0],3),np.uint8) for i,q in enumerate(qu): - o = damask.Orientation(q,crystal_structure).reduced() - in_memory[i] = np.uint8(o.IPFcolor(np.array(d))*255) + o = damask.Orientation(q,crystal_structure).reduced + in_memory[i] = np.uint8(o.IPF_color(np.array(d))*255) in_file = default.read_dataset(loc['color']) assert np.allclose(in_memory,in_file) @@ -319,4 +319,4 @@ class TestResult: def test_XDMF(self,tmp_path,single_phase): os.chdir(tmp_path) - single_phase.write_XDMF + single_phase.write_XDMF() diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 89173bb5a..21d0b5fae 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -6,94 +6,21 @@ import numpy as np from damask import Rotation from damask import _rotation - - n = 1100 atol=1.e-4 -scatter=1.e-2 - -@pytest.fixture -def default(): - """A set of n rotations (corner cases and random).""" - specials = np.array([ - [1.0, 0.0, 0.0, 0.0], - #---------------------- - [0.0, 1.0, 0.0, 0.0], - [0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 0.0, 1.0], - [0.0,-1.0, 0.0, 0.0], - [0.0, 0.0,-1.0, 0.0], - [0.0, 0.0, 0.0,-1.0], - #---------------------- - [1.0, 1.0, 0.0, 0.0], - [1.0, 0.0, 1.0, 0.0], - [1.0, 0.0, 0.0, 1.0], - [0.0, 1.0, 1.0, 0.0], - [0.0, 1.0, 0.0, 1.0], - [0.0, 0.0, 1.0, 1.0], - #---------------------- - [1.0,-1.0, 0.0, 0.0], - [1.0, 0.0,-1.0, 0.0], - [1.0, 0.0, 0.0,-1.0], - [0.0, 1.0,-1.0, 0.0], - [0.0, 1.0, 0.0,-1.0], - [0.0, 0.0, 1.0,-1.0], - #---------------------- - [0.0, 1.0,-1.0, 0.0], - [0.0, 1.0, 0.0,-1.0], - [0.0, 0.0, 1.0,-1.0], - #---------------------- - [0.0,-1.0,-1.0, 0.0], - [0.0,-1.0, 0.0,-1.0], - [0.0, 0.0,-1.0,-1.0], - #---------------------- - [1.0, 1.0, 1.0, 0.0], - [1.0, 1.0, 0.0, 1.0], - [1.0, 0.0, 1.0, 1.0], - [1.0,-1.0, 1.0, 0.0], - [1.0,-1.0, 0.0, 1.0], - [1.0, 0.0,-1.0, 1.0], - [1.0, 1.0,-1.0, 0.0], - [1.0, 1.0, 0.0,-1.0], - [1.0, 0.0, 1.0,-1.0], - [1.0,-1.0,-1.0, 0.0], - [1.0,-1.0, 0.0,-1.0], - [1.0, 0.0,-1.0,-1.0], - #---------------------- - [0.0, 1.0, 1.0, 1.0], - [0.0, 1.0,-1.0, 1.0], - [0.0, 1.0, 1.0,-1.0], - [0.0,-1.0, 1.0, 1.0], - [0.0,-1.0,-1.0, 1.0], - [0.0,-1.0, 1.0,-1.0], - [0.0,-1.0,-1.0,-1.0], - #---------------------- - [1.0, 1.0, 1.0, 1.0], - [1.0,-1.0, 1.0, 1.0], - [1.0, 1.0,-1.0, 1.0], - [1.0, 1.0, 1.0,-1.0], - [1.0,-1.0,-1.0, 1.0], - [1.0,-1.0, 1.0,-1.0], - [1.0, 1.0,-1.0,-1.0], - [1.0,-1.0,-1.0,-1.0], - ]) - specials /= np.linalg.norm(specials,axis=1).reshape(-1,1) - specials_scatter = specials + np.broadcast_to(np.random.rand(4)*scatter,specials.shape) - specials_scatter /= np.linalg.norm(specials_scatter,axis=1).reshape(-1,1) - specials_scatter[specials_scatter[:,0]<0]*=-1 - - return [Rotation.from_quaternion(s) for s in specials] + \ - [Rotation.from_quaternion(s) for s in specials_scatter] + \ - [Rotation.from_random() for _ in range(n-len(specials)-len(specials_scatter))] @pytest.fixture def reference_dir(reference_dir_base): """Directory containing reference results.""" return os.path.join(reference_dir_base,'Rotation') +@pytest.fixture +def set_of_rotations(set_of_quaternions): + return [Rotation.from_quaternion(s) for s in set_of_quaternions] + #################################################################################################### -# Code below available according to the following conditions on https://github.com/MarDiehl/3Drotations +# Code below available according to the following conditions #################################################################################################### # Copyright (c) 2017-2019, Martin Diehl/Max-Planck-Institut für Eisenforschung GmbH # Copyright (c) 2013-2014, Marc De Graef/Carnegie Mellon University @@ -567,9 +494,9 @@ class TestRotation: (Rotation._qu2ro,Rotation._ro2qu), (Rotation._qu2ho,Rotation._ho2qu), (Rotation._qu2cu,Rotation._cu2qu)]) - def test_quaternion_internal(self,default,forward,backward): + def test_quaternion_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from quaternion and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_quaternion() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -584,9 +511,9 @@ class TestRotation: (Rotation._om2ro,Rotation._ro2om), (Rotation._om2ho,Rotation._ho2om), (Rotation._om2cu,Rotation._cu2om)]) - def test_matrix_internal(self,default,forward,backward): + def test_matrix_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from rotation matrix and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_matrix() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -599,9 +526,9 @@ class TestRotation: (Rotation._eu2ro,Rotation._ro2eu), (Rotation._eu2ho,Rotation._ho2eu), (Rotation._eu2cu,Rotation._cu2eu)]) - def test_Eulers_internal(self,default,forward,backward): + def test_Eulers_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from Euler angles and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_Eulers() o = backward(forward(m)) u = np.array([np.pi*2,np.pi,np.pi*2]) @@ -619,9 +546,9 @@ class TestRotation: (Rotation._ax2ro,Rotation._ro2ax), (Rotation._ax2ho,Rotation._ho2ax), (Rotation._ax2cu,Rotation._cu2ax)]) - def test_axis_angle_internal(self,default,forward,backward): + def test_axis_angle_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from axis angle angles pair and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_axis_angle() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -636,10 +563,10 @@ class TestRotation: (Rotation._ro2ax,Rotation._ax2ro), (Rotation._ro2ho,Rotation._ho2ro), (Rotation._ro2cu,Rotation._cu2ro)]) - def test_Rodrigues_internal(self,default,forward,backward): + def test_Rodrigues_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from Rodrigues-Frank vector and back.""" cutoff = np.tan(np.pi*.5*(1.-1e-4)) - for rot in default: + for rot in set_of_rotations: m = rot.as_Rodrigues() o = backward(forward(m)) ok = np.allclose(np.clip(m,None,cutoff),np.clip(o,None,cutoff),atol=atol) @@ -653,9 +580,9 @@ class TestRotation: (Rotation._ho2ax,Rotation._ax2ho), (Rotation._ho2ro,Rotation._ro2ho), (Rotation._ho2cu,Rotation._cu2ho)]) - def test_homochoric_internal(self,default,forward,backward): + def test_homochoric_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from homochoric vector and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_homochoric() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -668,9 +595,9 @@ class TestRotation: (Rotation._cu2ax,Rotation._ax2cu), (Rotation._cu2ro,Rotation._ro2cu), (Rotation._cu2ho,Rotation._ho2cu)]) - def test_cubochoric_internal(self,default,forward,backward): + def test_cubochoric_internal(self,set_of_rotations,forward,backward): """Ensure invariance of conversion from cubochoric vector and back.""" - for rot in default: + for rot in set_of_rotations: m = rot.as_cubochoric() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) @@ -684,9 +611,9 @@ class TestRotation: (Rotation._qu2ax,qu2ax), (Rotation._qu2ro,qu2ro), (Rotation._qu2ho,qu2ho)]) - def test_quaternion_vectorization(self,default,vectorized,single): + def test_quaternion_vectorization(self,set_of_quaternions,vectorized,single): """Check vectorized implementation for quaternion against single point calculation.""" - qu = np.array([rot.as_quaternion() for rot in default]) + qu = np.array(set_of_quaternions) vectorized(qu.reshape(qu.shape[0]//2,-1,4)) co = vectorized(qu) for q,c in zip(qu,co): @@ -697,9 +624,9 @@ class TestRotation: @pytest.mark.parametrize('vectorized, single',[(Rotation._om2qu,om2qu), (Rotation._om2eu,om2eu), (Rotation._om2ax,om2ax)]) - def test_matrix_vectorization(self,default,vectorized,single): + def test_matrix_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for rotation matrix against single point calculation.""" - om = np.array([rot.as_matrix() for rot in default]) + om = np.array([rot.as_matrix() for rot in set_of_rotations]) vectorized(om.reshape(om.shape[0]//2,-1,3,3)) co = vectorized(om) for o,c in zip(om,co): @@ -710,9 +637,9 @@ class TestRotation: (Rotation._eu2om,eu2om), (Rotation._eu2ax,eu2ax), (Rotation._eu2ro,eu2ro)]) - def test_Eulers_vectorization(self,default,vectorized,single): + def test_Eulers_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for Euler angles against single point calculation.""" - eu = np.array([rot.as_Eulers() for rot in default]) + eu = np.array([rot.as_Eulers() for rot in set_of_rotations]) vectorized(eu.reshape(eu.shape[0]//2,-1,3)) co = vectorized(eu) for e,c in zip(eu,co): @@ -723,9 +650,9 @@ class TestRotation: (Rotation._ax2om,ax2om), (Rotation._ax2ro,ax2ro), (Rotation._ax2ho,ax2ho)]) - def test_axis_angle_vectorization(self,default,vectorized,single): + def test_axis_angle_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for axis angle pair against single point calculation.""" - ax = np.array([rot.as_axis_angle() for rot in default]) + ax = np.array([rot.as_axis_angle() for rot in set_of_rotations]) vectorized(ax.reshape(ax.shape[0]//2,-1,4)) co = vectorized(ax) for a,c in zip(ax,co): @@ -735,9 +662,9 @@ class TestRotation: @pytest.mark.parametrize('vectorized, single',[(Rotation._ro2ax,ro2ax), (Rotation._ro2ho,ro2ho)]) - def test_Rodrigues_vectorization(self,default,vectorized,single): + def test_Rodrigues_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for Rodrigues-Frank vector against single point calculation.""" - ro = np.array([rot.as_Rodrigues() for rot in default]) + ro = np.array([rot.as_Rodrigues() for rot in set_of_rotations]) vectorized(ro.reshape(ro.shape[0]//2,-1,4)) co = vectorized(ro) for r,c in zip(ro,co): @@ -746,9 +673,9 @@ class TestRotation: @pytest.mark.parametrize('vectorized, single',[(Rotation._ho2ax,ho2ax), (Rotation._ho2cu,ho2cu)]) - def test_homochoric_vectorization(self,default,vectorized,single): + def test_homochoric_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for homochoric vector against single point calculation.""" - ho = np.array([rot.as_homochoric() for rot in default]) + ho = np.array([rot.as_homochoric() for rot in set_of_rotations]) vectorized(ho.reshape(ho.shape[0]//2,-1,3)) co = vectorized(ho) for h,c in zip(ho,co): @@ -756,9 +683,9 @@ class TestRotation: assert np.allclose(single(h),c) and np.allclose(single(h),vectorized(h)) @pytest.mark.parametrize('vectorized, single',[(Rotation._cu2ho,cu2ho)]) - def test_cubochoric_vectorization(self,default,vectorized,single): + def test_cubochoric_vectorization(self,set_of_rotations,vectorized,single): """Check vectorized implementation for cubochoric vector against single point calculation.""" - cu = np.array([rot.as_cubochoric() for rot in default]) + cu = np.array([rot.as_cubochoric() for rot in set_of_rotations]) vectorized(cu.reshape(cu.shape[0]//2,-1,3)) co = vectorized(cu) for u,c in zip(cu,co): @@ -766,8 +693,8 @@ class TestRotation: assert np.allclose(single(u),c) and np.allclose(single(u),vectorized(u)) @pytest.mark.parametrize('degrees',[True,False]) - def test_Eulers(self,default,degrees): - for rot in default: + def test_Eulers(self,set_of_rotations,degrees): + for rot in set_of_rotations: m = rot.as_quaternion() o = Rotation.from_Eulers(rot.as_Eulers(degrees),degrees).as_quaternion() ok = np.allclose(m,o,atol=atol) @@ -779,9 +706,9 @@ class TestRotation: @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('normalise',[True,False]) @pytest.mark.parametrize('degrees',[True,False]) - def test_axis_angle(self,default,degrees,normalise,P): + def test_axis_angle(self,set_of_rotations,degrees,normalise,P): c = np.array([P*-1,P*-1,P*-1,1.]) - for rot in default: + for rot in set_of_rotations: m = rot.as_Eulers() o = Rotation.from_axis_angle(rot.as_axis_angle(degrees)*c,degrees,normalise,P).as_Eulers() u = np.array([np.pi*2,np.pi,np.pi*2]) @@ -793,8 +720,8 @@ class TestRotation: print(m,o,rot.as_quaternion()) assert ok and (np.zeros(3)-1.e-9 <= o).all() and (o <= np.array([np.pi*2.,np.pi,np.pi*2.])+1.e-9).all() - def test_matrix(self,default): - for rot in default: + def test_matrix(self,set_of_rotations): + for rot in set_of_rotations: m = rot.as_axis_angle() o = Rotation.from_axis_angle(rot.as_axis_angle()).as_axis_angle() ok = np.allclose(m,o,atol=atol) @@ -805,9 +732,9 @@ class TestRotation: @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('normalise',[True,False]) - def test_Rodrigues(self,default,normalise,P): + def test_Rodrigues(self,set_of_rotations,normalise,P): c = np.array([P*-1,P*-1,P*-1,1.]) - for rot in default: + for rot in set_of_rotations: m = rot.as_matrix() o = Rotation.from_Rodrigues(rot.as_Rodrigues()*c,normalise,P).as_matrix() ok = np.allclose(m,o,atol=atol) @@ -815,9 +742,9 @@ class TestRotation: assert ok and np.isclose(np.linalg.det(o),1.0) @pytest.mark.parametrize('P',[1,-1]) - def test_homochoric(self,default,P): + def test_homochoric(self,set_of_rotations,P): cutoff = np.tan(np.pi*.5*(1.-1e-4)) - for rot in default: + for rot in set_of_rotations: m = rot.as_Rodrigues() o = Rotation.from_homochoric(rot.as_homochoric()*P*-1,P).as_Rodrigues() ok = np.allclose(np.clip(m,None,cutoff),np.clip(o,None,cutoff),atol=atol) @@ -826,8 +753,8 @@ class TestRotation: assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) @pytest.mark.parametrize('P',[1,-1]) - def test_cubochoric(self,default,P): - for rot in default: + def test_cubochoric(self,set_of_rotations,P): + for rot in set_of_rotations: m = rot.as_homochoric() o = Rotation.from_cubochoric(rot.as_cubochoric()*P*-1,P).as_homochoric() ok = np.allclose(m,o,atol=atol) @@ -836,9 +763,9 @@ class TestRotation: @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('accept_homomorph',[True,False]) - def test_quaternion(self,default,P,accept_homomorph): + def test_quaternion(self,set_of_rotations,P,accept_homomorph): c = np.array([1,P*-1,P*-1,P*-1]) * (-1 if accept_homomorph else 1) - for rot in default: + for rot in set_of_rotations: m = rot.as_cubochoric() o = Rotation.from_quaternion(rot.as_quaternion()*c,accept_homomorph,P).as_cubochoric() ok = np.allclose(m,o,atol=atol) @@ -848,8 +775,8 @@ class TestRotation: assert ok and o.max() < np.pi**(2./3.)*0.5+1.e-9 @pytest.mark.parametrize('reciprocal',[True,False]) - def test_basis(self,default,reciprocal): - for rot in default: + def test_basis(self,set_of_rotations,reciprocal): + for rot in set_of_rotations: om = rot.as_matrix() + 0.1*np.eye(3) rot = Rotation.from_basis(om,False,reciprocal=reciprocal) assert np.isclose(np.linalg.det(rot.as_matrix()),1.0) @@ -923,8 +850,8 @@ class TestRotation: @pytest.mark.parametrize('data',[np.random.rand(5,3), np.random.rand(5,3,3), np.random.rand(5,3,3,3,3)]) - def test_rotate_vectorization(self,default,data): - for rot in default: + def test_rotate_vectorization(self,set_of_rotations,data): + for rot in set_of_rotations: v = rot.broadcast_to((5,)) @ data for i in range(data.shape[0]): print(i-data[i]) @@ -978,3 +905,15 @@ class TestRotation: def test_misorientation(self): R = Rotation.from_random() assert np.allclose(R.misorientation(R).as_matrix(),np.eye(3)) + + def test_misorientation360(self): + R_1 = Rotation() + R_2 = Rotation.from_Eulers([360,0,0],degrees=True) + assert np.allclose(R_1.misorientation(R_2).as_matrix(),np.eye(3)) + + @pytest.mark.parametrize('angle',[10,20,30,40,50,60,70,80,90,100,120]) + def test_average(self,angle): + R_1 = Rotation.from_axis_angle([0,0,1,10],degrees=True) + R_2 = Rotation.from_axis_angle([0,0,1,angle],degrees=True) + avg_angle = R_1.average(R_2).as_axis_angle(degrees=True,pair=True)[1] + assert np.isclose(avg_angle,10+(angle-10)/2.) diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index ef92a09f1..91ff4033c 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -1,3 +1,7 @@ +import os +import filecmp +import time + import pytest import numpy as np @@ -17,7 +21,7 @@ class TestVTK: origin = np.random.random(3) v = VTK.from_rectilinearGrid(grid,size,origin) string = v.__repr__() - v.write(tmp_path/'rectilinearGrid') + v.write(tmp_path/'rectilinearGrid',False) vtr = VTK.from_file(tmp_path/'rectilinearGrid.vtr') with open(tmp_path/'rectilinearGrid.vtk','w') as f: f.write(string) @@ -25,10 +29,10 @@ class TestVTK: assert(string == vtr.__repr__() == vtk.__repr__()) def test_polyData(self,tmp_path): - points = np.random.rand(3,100) + points = np.random.rand(100,3) v = VTK.from_polyData(points) string = v.__repr__() - v.write(tmp_path/'polyData') + v.write(tmp_path/'polyData',False) vtp = VTK.from_file(tmp_path/'polyData.vtp') with open(tmp_path/'polyData.vtk','w') as f: f.write(string) @@ -47,14 +51,30 @@ class TestVTK: connectivity = np.random.choice(np.arange(n),n,False).reshape(-1,n) v = VTK.from_unstructuredGrid(nodes,connectivity,cell_type) string = v.__repr__() - v.write(tmp_path/'unstructuredGrid') + v.write(tmp_path/'unstructuredGrid',False) vtu = VTK.from_file(tmp_path/'unstructuredGrid.vtu') with open(tmp_path/'unstructuredGrid.vtk','w') as f: f.write(string) vtk = VTK.from_file(tmp_path/'unstructuredGrid.vtk','unstructuredgrid') assert(string == vtu.__repr__() == vtk.__repr__()) - @pytest.mark.parametrize('name,dataset_type',[('this_file_does_not_exist.vtk',None), + + def test_parallel_out(self,tmp_path): + points = np.random.rand(102,3) + v = VTK.from_polyData(points) + fname_s = tmp_path/'single.vtp' + fname_p = tmp_path/'parallel.vtp' + v.write(fname_s,False) + v.write(fname_p,True) + for i in range(10): + if os.path.isfile(fname_p) and filecmp.cmp(fname_s,fname_p): + assert(True) + return + time.sleep(.5) + assert(False) + + + @pytest.mark.parametrize('name,dataset_type',[('this_file_does_not_exist.vtk', None), ('this_file_does_not_exist.vtk','vtk'), ('this_file_does_not_exist.vtx', None)]) def test_invalid_dataset_type(self,dataset_type,name): diff --git a/src/DAMASK_interface.f90 b/src/DAMASK_interface.f90 index 67c7d99da..1ebae3401 100644 --- a/src/DAMASK_interface.f90 +++ b/src/DAMASK_interface.f90 @@ -6,7 +6,7 @@ !> @brief Interfacing between the PETSc-based solvers and the material subroutines provided !! by DAMASK !> @details Interfacing between the PETSc-based solvers and the material subroutines provided -!> by DAMASK. Interpretating the command line arguments to get load case, geometry file, +!> by DAMASK. Interpreting the command line arguments to get load case, geometry file, !> and working directory. !-------------------------------------------------------------------------------------------------- #define PETSC_MAJOR 3 @@ -20,11 +20,11 @@ module DAMASK_interface use prec use system_routines - + implicit none private logical, volatile, public, protected :: & - SIGTERM, & !< termination signal + SIGTERM, & !< termination signal SIGUSR1, & !< 1. user-defined signal SIGUSR2 !< 2. user-defined signal integer, public, protected :: & @@ -159,14 +159,14 @@ subroutine DAMASK_interface_init call MPI_Type_size(MPI_INTEGER,typeSize,err) if (err /= 0) call quit(1) if (typeSize*8 /= bit_size(0)) then - write(6,'(a)') ' Mismatch between MPI and DAMASK integer' + write(6,'(a)') ' Mismatch between MPI and DAMASK integer' call quit(1) endif call MPI_Type_size(MPI_DOUBLE,typeSize,err) if (err /= 0) call quit(1) if (typeSize*8 /= storage_size(0.0_pReal)) then - write(6,'(a)') ' Mismatch between MPI and DAMASK real' + write(6,'(a)') ' Mismatch between MPI and DAMASK real' call quit(1) endif @@ -340,7 +340,7 @@ end function getGeometryFile !-------------------------------------------------------------------------------------------------- -!> @brief relative path of loadcase from command line arguments +!> @brief relative path of load case from command line arguments !-------------------------------------------------------------------------------------------------- function getLoadCaseFile(loadCaseParameter) diff --git a/src/constitutive.f90 b/src/constitutive.f90 index 7238eeb20..c8dfcf9d3 100644 --- a/src/constitutive.f90 +++ b/src/constitutive.f90 @@ -400,7 +400,7 @@ subroutine constitutive_init constitutive_source_maxSizeDotState = 0 PhaseLoop2:do ph = 1,material_Nphase !-------------------------------------------------------------------------------------------------- -! partition and inititalize state +! partition and initialize state plasticState(ph)%partionedState0 = plasticState(ph)%state0 plasticState(ph)%state = plasticState(ph)%partionedState0 forall(s = 1:phase_Nsources(ph)) @@ -475,7 +475,7 @@ end subroutine constitutive_dependentState !-------------------------------------------------------------------------------------------------- !> @brief contains the constitutive equation for calculating the velocity gradient -! ToDo: Discuss wheter it makes sense if crystallite handles the configuration conversion, i.e. +! ToDo: Discuss whether it makes sense if crystallite handles the configuration conversion, i.e. ! Mp in, dLp_dMp out !-------------------------------------------------------------------------------------------------- subroutine constitutive_LpAndItsTangents(Lp, dLp_dS, dLp_dFi, & @@ -686,7 +686,7 @@ end subroutine constitutive_SandItsTangents !-------------------------------------------------------------------------------------------------- !> @brief returns the 2nd Piola-Kirchhoff stress tensor and its tangent with respect to -!> the elastic and intermeidate deformation gradients using Hookes law +!> the elastic and intermediate deformation gradients using Hooke's law !-------------------------------------------------------------------------------------------------- subroutine constitutive_hooke_SandItsTangents(S, dS_dFe, dS_dFi, & Fe, Fi, ipc, ip, el) diff --git a/src/constitutive_plastic_nonlocal.f90 b/src/constitutive_plastic_nonlocal.f90 index f31d7b3ed..5f9f63f24 100644 --- a/src/constitutive_plastic_nonlocal.f90 +++ b/src/constitutive_plastic_nonlocal.f90 @@ -37,7 +37,7 @@ submodule(constitutive) plastic_nonlocal ! BEGIN DEPRECATED integer, dimension(:,:,:), allocatable :: & iRhoU, & !< state indices for unblocked density - iV, & !< state indices for dislcation velocities + iV, & !< state indices for dislocation velocities iD !< state indices for stable dipole height !END DEPRECATED diff --git a/src/kinematics_thermal_expansion.f90 b/src/kinematics_thermal_expansion.f90 index 39a6bb61f..63da6eb51 100644 --- a/src/kinematics_thermal_expansion.f90 +++ b/src/kinematics_thermal_expansion.f90 @@ -100,7 +100,7 @@ end function kinematics_thermal_expansion_initialStrain !-------------------------------------------------------------------------------------------------- -!> @brief contains the constitutive equation for calculating the velocity gradient +!> @brief constitutive equation for calculating the velocity gradient !-------------------------------------------------------------------------------------------------- subroutine kinematics_thermal_expansion_LiAndItsTangent(Li, dLi_dTstar, ipc, ip, el) diff --git a/src/list.f90 b/src/list.f90 index 1d1bd5fff..901bb4d7c 100644 --- a/src/list.f90 +++ b/src/list.f90 @@ -1,18 +1,18 @@ !------------------------------------------------------------------------------------------------- !> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH -!> @brief linked list +!> @brief Linked list !-------------------------------------------------------------------------------------------------- module list use prec use IO - + implicit none - private + private type, private :: tPartitionedString character(len=:), allocatable :: val integer, dimension(:), allocatable :: pos end type tPartitionedString - + type, public :: tPartitionedStringList type(tPartitionedString) :: string type(tPartitionedStringList), pointer :: next => null() @@ -20,31 +20,31 @@ module list procedure :: add => add procedure :: show => show procedure :: free => free - + ! currently, a finalize is needed for all shapes of tPartitionedStringList. ! with Fortran 2015, we can define one recursive elemental function ! https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/543326 final :: finalize, & - finalizeArray - + finalizeArray + procedure :: keyExists => keyExists procedure :: countKeys => countKeys - + procedure :: getFloat => getFloat procedure :: getInt => getInt procedure :: getString => getString - + procedure :: getFloats => getFloats procedure :: getInts => getInts procedure :: getStrings => getStrings - + end type tPartitionedStringList contains !-------------------------------------------------------------------------------------------------- !> @brief add element -!> @details Adds a string together with the start/end position of chunks in this string. The new +!> @details Adds a string together with the start/end position of chunks in this string. The new !! element is added at the end of the list. Empty strings are not added. All strings are converted !! to lower case. The data is not stored in the new element but in the current. !-------------------------------------------------------------------------------------------------- @@ -177,7 +177,7 @@ end function countKeys !-------------------------------------------------------------------------------------------------- !> @brief gets float value of for a given key from a linked list -!> @details gets the last value if the key occurs more than once. If key is not found exits with +!> @details gets the last value if the key occurs more than once. If key is not found exits with !! error unless default is given !-------------------------------------------------------------------------------------------------- real(pReal) function getFloat(this,key,defaultVal) @@ -187,11 +187,11 @@ real(pReal) function getFloat(this,key,defaultVal) real(pReal), intent(in), optional :: defaultVal type(tPartitionedStringList), pointer :: item logical :: found - + getFloat = huge(1.0) ! suppress warning about unitialized value found = present(defaultVal) if (found) getFloat = defaultVal - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then @@ -209,7 +209,7 @@ end function getFloat !-------------------------------------------------------------------------------------------------- !> @brief gets integer value of for a given key from a linked list -!> @details gets the last value if the key occurs more than once. If key is not found exits with +!> @details gets the last value if the key occurs more than once. If key is not found exits with !! error unless default is given !-------------------------------------------------------------------------------------------------- integer function getInt(this,key,defaultVal) @@ -223,7 +223,7 @@ integer function getInt(this,key,defaultVal) getInt = huge(1) ! suppress warning about unitialized value found = present(defaultVal) if (found) getInt = defaultVal - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then @@ -241,12 +241,12 @@ end function getInt !-------------------------------------------------------------------------------------------------- !> @brief gets string value of for a given key from a linked list -!> @details gets the last value if the key occurs more than once. If key is not found exits with -!! error unless default is given. If raw is true, the the complete string is returned, otherwise +!> @details gets the last value if the key occurs more than once. If key is not found exits with +!! error unless default is given. If raw is true, the the complete string is returned, otherwise !! the individual chunks are returned !-------------------------------------------------------------------------------------------------- character(len=pStringLen) function getString(this,key,defaultVal,raw) - + class(tPartitionedStringList), target, intent(in) :: this character(len=*), intent(in) :: key character(len=*), intent(in), optional :: defaultVal @@ -259,19 +259,19 @@ character(len=pStringLen) function getString(this,key,defaultVal,raw) else whole = .false. endif - + found = present(defaultVal) if (found) then if (len_trim(defaultVal) > len(getString)) call IO_error(0,ext_msg='getString') getString = trim(defaultVal) endif - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then found = .true. if (item%string%pos(1) < 2) call IO_error(143,ext_msg=key) - + if (whole) then getString = trim(item%string%val(item%string%pos(4):)) ! raw string starting a second chunk else @@ -280,7 +280,7 @@ character(len=pStringLen) function getString(this,key,defaultVal,raw) endif item => item%next enddo - + if (.not. found) call IO_error(140,ext_msg=key) end function getString @@ -292,7 +292,7 @@ end function getString !! values from the last occurrence. If key is not found exits with error unless default is given. !-------------------------------------------------------------------------------------------------- function getFloats(this,key,defaultVal,requiredSize) - + real(pReal), dimension(:), allocatable :: getFloats class(tPartitionedStringList), target, intent(in) :: this character(len=*), intent(in) :: key @@ -302,12 +302,12 @@ function getFloats(this,key,defaultVal,requiredSize) integer :: i logical :: found, & cumulative - + cumulative = (key(1:1) == '(' .and. key(len_trim(key):len_trim(key)) == ')') found = .false. - + allocate(getFloats(0)) - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then @@ -320,7 +320,7 @@ function getFloats(this,key,defaultVal,requiredSize) endif item => item%next enddo - + if (.not. found) then if (present(defaultVal)) then; getFloats = defaultVal; else; call IO_error(140,ext_msg=key); endif endif @@ -337,7 +337,7 @@ end function getFloats !! values from the last occurrence. If key is not found exits with error unless default is given. !-------------------------------------------------------------------------------------------------- function getInts(this,key,defaultVal,requiredSize) - + integer, dimension(:), allocatable :: getInts class(tPartitionedStringList), target, intent(in) :: this character(len=*), intent(in) :: key @@ -347,12 +347,12 @@ function getInts(this,key,defaultVal,requiredSize) integer :: i logical :: found, & cumulative - + cumulative = (key(1:1) == '(' .and. key(len_trim(key):len_trim(key)) == ')') found = .false. - + allocate(getInts(0)) - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then @@ -365,7 +365,7 @@ function getInts(this,key,defaultVal,requiredSize) endif item => item%next enddo - + if (.not. found) then if (present(defaultVal)) then; getInts = defaultVal; else; call IO_error(140,ext_msg=key); endif endif @@ -383,7 +383,7 @@ end function getInts !! If raw is true, the the complete string is returned, otherwise the individual chunks are returned !-------------------------------------------------------------------------------------------------- function getStrings(this,key,defaultVal,raw) - + character(len=pStringLen),dimension(:), allocatable :: getStrings class(tPartitionedStringList),target, intent(in) :: this character(len=*), intent(in) :: key @@ -395,7 +395,7 @@ function getStrings(this,key,defaultVal,raw) logical :: found, & whole, & cumulative - + cumulative = (key(1:1) == '(' .and. key(len_trim(key):len_trim(key)) == ')') if (present(raw)) then whole = raw @@ -403,14 +403,14 @@ function getStrings(this,key,defaultVal,raw) whole = .false. endif found = .false. - + item => this do while (associated(item%next)) if (trim(IO_stringValue(item%string%val,item%string%pos,1)) == trim(key)) then found = .true. if (allocated(getStrings) .and. .not. cumulative) deallocate(getStrings) if (item%string%pos(1) < 2) call IO_error(143,ext_msg=key) - + notAllocated: if (.not. allocated(getStrings)) then if (whole) then str = item%string%val(item%string%pos(4):) @@ -437,7 +437,7 @@ function getStrings(this,key,defaultVal,raw) endif item => item%next enddo - + if (.not. found) then if (present(defaultVal)) then if (len(defaultVal) > len(getStrings)) call IO_error(0,ext_msg='getStrings') diff --git a/src/source_damage_isoDuctile.f90 b/src/source_damage_isoDuctile.f90 index 461f3797d..1578fe50c 100644 --- a/src/source_damage_isoDuctile.f90 +++ b/src/source_damage_isoDuctile.f90 @@ -1,7 +1,7 @@ !-------------------------------------------------------------------------------------------------- !> @author Pratheek Shanthraj, Max-Planck-Institut für Eisenforschung GmbH !> @author Luv Sharma, Max-Planck-Institut für Eisenforschung GmbH -!> @brief material subroutine incoprorating isotropic ductile damage source mechanism +!> @brief material subroutine incorporating isotropic ductile damage source mechanism !> @details to be done !-------------------------------------------------------------------------------------------------- module source_damage_isoDuctile diff --git a/src/system_routines.f90 b/src/system_routines.f90 index d29787909..450dfe5b1 100644 --- a/src/system_routines.f90 +++ b/src/system_routines.f90 @@ -1,14 +1,14 @@ !-------------------------------------------------------------------------------------------------- -!> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH -!> @brief provides wrappers to C routines +!> @author Martin Diehl, Max-Planck-Institut für Eisenforschung GmbH +!> @brief Wrappers to C routines for system operations !-------------------------------------------------------------------------------------------------- module system_routines use, intrinsic :: ISO_C_Binding - - use prec - + + use prec + implicit none - + public :: & signalterm_C, & signalusr1_C, & @@ -17,74 +17,74 @@ module system_routines getCWD, & getHostName, & setCWD - + interface - + function isDirectory_C(path) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_INT, & C_CHAR use prec - + integer(C_INT) :: isDirectory_C character(kind=C_CHAR), dimension(pPathLen), intent(in) :: path ! C string is an array end function isDirectory_C - + subroutine getCurrentWorkDir_C(path, stat) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_INT, & C_CHAR use prec - + character(kind=C_CHAR), dimension(pPathLen), intent(out) :: path ! C string is an array integer(C_INT), intent(out) :: stat end subroutine getCurrentWorkDir_C - + subroutine getHostName_C(str, stat) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_INT, & C_CHAR use prec - + character(kind=C_CHAR), dimension(pStringLen), intent(out) :: str ! C string is an array integer(C_INT), intent(out) :: stat end subroutine getHostName_C - + function chdir_C(path) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_INT, & C_CHAR use prec - + integer(C_INT) :: chdir_C character(kind=C_CHAR), dimension(pPathLen), intent(in) :: path ! C string is an array end function chdir_C - + subroutine signalterm_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_FUNPTR - + type(C_FUNPTR), intent(in), value :: handler end subroutine signalterm_C - + subroutine signalusr1_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_FUNPTR - + type(C_FUNPTR), intent(in), value :: handler end subroutine signalusr1_C - + subroutine signalusr2_C(handler) bind(C) use, intrinsic :: ISO_C_Binding, only: & C_FUNPTR - + type(C_FUNPTR), intent(in), value :: handler end subroutine signalusr2_C - + end interface contains @@ -96,8 +96,8 @@ logical function isDirectory(path) character(len=*), intent(in) :: path character(kind=C_CHAR), dimension(pPathLen) :: strFixedLength ! C string as array - integer :: i - + integer :: i + strFixedLength = repeat(C_NULL_CHAR,len(strFixedLength)) do i=1,len(path) ! copy array components strFixedLength(i)=path(i:i) @@ -116,9 +116,9 @@ function getCWD() character(len=:), allocatable :: getCWD integer(C_INT) :: stat integer :: i - + call getCurrentWorkDir_C(charArray,stat) - + if (stat /= 0_C_INT) then getCWD = 'Error occured when getting currend working directory' else @@ -145,7 +145,7 @@ function getHostName() character(len=:), allocatable :: getHostName integer(C_INT) :: stat integer :: i - + call getHostName_C(charArray,stat) if (stat /= 0_C_INT) then @@ -173,7 +173,7 @@ logical function setCWD(path) character(len=*), intent(in) :: path character(kind=C_CHAR), dimension(pPathLen) :: strFixedLength ! C string is an array integer :: i - + strFixedLength = repeat(C_NULL_CHAR,len(strFixedLength)) do i=1,len(path) ! copy array components strFixedLength(i)=path(i:i) diff --git a/src/thermal_conduction.f90 b/src/thermal_conduction.f90 index be72c07b6..82eed4cc4 100644 --- a/src/thermal_conduction.f90 +++ b/src/thermal_conduction.f90 @@ -73,7 +73,7 @@ end subroutine thermal_conduction_init !-------------------------------------------------------------------------------------------------- -!> @brief returns heat generation rate +!> @brief return heat generation rate !-------------------------------------------------------------------------------------------------- subroutine thermal_conduction_getSourceAndItsTangent(Tdot, dTdot_dT, T, ip, el) @@ -132,7 +132,7 @@ end subroutine thermal_conduction_getSourceAndItsTangent !-------------------------------------------------------------------------------------------------- -!> @brief returns homogenized thermal conductivity in reference configuration +!> @brief return homogenized thermal conductivity in reference configuration !-------------------------------------------------------------------------------------------------- function thermal_conduction_getConductivity(ip,el) diff --git a/src/thermal_isothermal.f90 b/src/thermal_isothermal.f90 index 630e8db6d..ceb714740 100644 --- a/src/thermal_isothermal.f90 +++ b/src/thermal_isothermal.f90 @@ -8,14 +8,14 @@ module thermal_isothermal implicit none public - + contains !-------------------------------------------------------------------------------------------------- -!> @brief allocates all neccessary fields, reads information from material configuration file +!> @brief allocates fields, reads information from material configuration file !-------------------------------------------------------------------------------------------------- subroutine thermal_isothermal_init - + integer :: h,NofMyHomog write(6,'(/,a)') ' <<<+- thermal_'//THERMAL_isothermal_label//' init -+>>>'; flush(6) @@ -28,7 +28,7 @@ subroutine thermal_isothermal_init allocate(thermalState(h)%state0 (0,NofMyHomog)) allocate(thermalState(h)%subState0(0,NofMyHomog)) allocate(thermalState(h)%state (0,NofMyHomog)) - + deallocate(temperature (h)%p) allocate (temperature (h)%p(1), source=thermal_initialT(h)) deallocate(temperatureRate(h)%p)