# -*- coding: utf-8 -*-
# =============================================================================
# Copyright (c) 2020, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
# Written by Saransh Singh <saransh1@llnl.gov> and others.
# LLNL-CODE-529294.
# All rights reserved.
#
# This file is part of HEXRD. For details on dowloading the source,
# see the file COPYING.
#
# Please also see the file LICENSE.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License (as published by the Free
# Software Foundation) version 2.1 dated February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program (see file LICENSE); if not, write to
# the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA 02111-1307 USA or visit <http://www.gnu.org/licenses/>.
# =============================================================================
from hexrd import constants
import numpy as np
from hexrd.ipfcolor import colorspace
eps = constants.sqrt_epsf
'''
In this section we will list the vertices of the spherical triangles
for each of the point group. this will be in the form of a dictionary
conforming to the symbols in unitcell.py
the entries are organized as follows:
key : point group symmetry
entry 0 in list: number of SST triangles needed to specify the
fundamental region. this could have values 0, 1 or 2. If it is 0,
then the whole sphere is valid (either upper or ower or both depending
on symmetry). If this is 1, then only 1 triangle is needed. If two, then
two triangles are needed i.e. 4 coordinates
entry 1 in list: specify coordinates of the triangle(s). this will be
empty array if size is 0 in previous entry. If size in previous entry is 1,
then this will 3x3. Finally if size in previous entry is 2, this will be 3x4.
entry 2 in list is the connectivity of the triangles. This is only useful for
the cases of T, Th, O and symmetry groups.
entry 3 in list: if both upper anf lower hemispheres are to be considered. For
the case of x-rays only upper hemisphere is considered because of Friedel's law,
which imposes and artificial inversion symmetry such that hkl and -hkl can't be
distinguished. However, for the formulation is general and can handle all symmetry
groups. This will say 'upper' or 'both' depending on what hemisphere is considered.
there are no triangles for the triclininc cases and needs to be handles differently
'''
pg2vertex = {
'c1': [3, np.array([[0., 0., 1.],
[1., 0., 0.],
[-0.5, np.sqrt(3.)/2., 0.],
[-0.5, -np.sqrt(3.)/2., 0.]]).T,
np.array([[0, 1, 2], [0, 2, 3], [0, 3, 1]]).T, 'both'],
# supergroup 00 in our convention
'ci': [3, np.array([[0., 0., 1.],
[1.0, 0., 0.],
[-0.5, np.sqrt(3.)/2., 0.],
[-0.5, -np.sqrt(3.)/2., 0.]]).T,
np.array([[0, 1, 2], [0, 2, 3], [0, 3, 1]]).T, 'upper'],
'c2': [2, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.],
[-1., 0., 0.]]).T,
np.array([[0, 1, 2], [0, 2, 3]]).T,
'both'],
# supergroup 1 in our convention
'cs': [3, np.array([[0., 0., 1.],
[1., 0., 0.],
[-0.5, np.sqrt(3.)/2., 0.],
[-0.5, -np.sqrt(3.)/2., 0.]]).T,
np.array([[0, 1, 2], [0, 2, 3], [0, 3, 1]]).T,
'upper'],
'c2h': [2, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.],
[-1., 0., 0.]]).T,
np.array([[0, 1, 2], [0, 2, 3]]).T,
'upper'],
'd2': [2, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.],
[-1., 0., 0.]]).T,
np.array([[0, 1, 2], [0, 2, 3]]).T,
'upper'],
# supergroup 2 in our convention
'c2v': [2, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.],
[0., 0., -1.]]).T,
np.array([[0, 1, 2], [3, 1, 2]]).T,
'both'],
# supergroup 3 in our convention
'd2h': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
'c4': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'both'],
# supergroup 01 in our convention
's4': [2, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.],
[-1., 0., 0.]]).T,
np.array([[0, 1, 2], [0, 2, 3]]).T,
'upper'],
'c4h': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.]]),
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
'd4': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.0]]),
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
# supergroup 4 in our convention
'c4v': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[1./np.sqrt(2.), 1./np.sqrt(2.), 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'both'],
'd2d': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[0., 1., 0.]]),
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
# supergroup 5 in our convention
'd4h': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[1./np.sqrt(2.), 1./np.sqrt(2.), 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
'c3': [1, np.array([[0., 0., 1.],
[np.sqrt(3.)/2., -0.5, 0.],
[0., 1., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'both'],
# supergroup 02 in our convention
's6': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[-0.5, np.sqrt(3.)/2., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
'd3': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[-0.5, np.sqrt(3.)/2., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
# supergroup 6 in our convention
'c3v': [1, np.array([[0., 0., 1.],
[np.sqrt(3.)/2., -0.5, 0.],
[np.sqrt(3.)/2., 0.5, 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'both'],
'd3d': [1, np.array([[0., 0., 1.],
[np.sqrt(3.)/2., -0.5, 0.],
[np.sqrt(3.)/2., 0.5, 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
'c6': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[0.5, np.sqrt(3.)/2., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'both'],
'c3h': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[-0.5, np.sqrt(3.)/2., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
'c6h': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[0.5, np.sqrt(3.)/2., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
'd6': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[0.5, np.sqrt(3.)/2., 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'both'],
# supergroup 7 in our convention
'c6v': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[np.sqrt(3.)/2., 0.5, 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'both'],
# supergroup 8 in our convention
'd3h': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[np.sqrt(3.)/2., 0.5, 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
# supergroup 9 in our convention
'd6h': [1, np.array([[0., 0., 1.],
[1., 0., 0.],
[np.sqrt(3.)/2., 0.5, 0.]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
# Special case with 2 triangles
't': [2, np.array([[0., 0., 1.],
[1./np.sqrt(3.), -1./np.sqrt(3.), 1./np.sqrt(3.)],
[1./np.sqrt(3.), 1./np.sqrt(3.), 1./np.sqrt(3.)],
[1., 0., 0.]]).T,
np.array([[0, 1, 2], [1, 3, 2]]).T,
'upper'],
# Special case with two triangles
'th': [2, np.array([[0., 0., 1.],
[1./np.sqrt(2.), 0., 1./np.sqrt(2.)],
[1./np.sqrt(3.), 1./np.sqrt(3.), 1./np.sqrt(3.)],
[0., 1./np.sqrt(2.), 1./np.sqrt(2.)]]).T,
np.array([[0, 1, 2], [0, 2, 3]]).T,
'upper'],
# Special case with two triangles, same as Th
'o': [2, np.array([[0., 0., 1.],
[1./np.sqrt(2.), 0., 1./np.sqrt(2.)],
[1./np.sqrt(3.), 1./np.sqrt(3.), 1./np.sqrt(3.)],
[0., 1./np.sqrt(2.), 1./np.sqrt(2.)]]).T,
np.array([[0, 1, 2], [0, 2, 3]]).T,
'upper'],
# supergroup 10 in our convention
'td': [1, np.array([[0., 0., 1.],
[1./np.sqrt(3.), -1./np.sqrt(3.), 1./np.sqrt(3.)],
[1./np.sqrt(3.), 1./np.sqrt(3.), 1./np.sqrt(3.)]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper'],
# supergroup 11 in our convention
'oh': [1, np.array([[0., 0., 1.],
[1./np.sqrt(2.), 0., 1./np.sqrt(2.)],
[1./np.sqrt(3.), 1./np.sqrt(3.), 1./np.sqrt(3.)]]).T,
np.atleast_2d(np.array([0, 1, 2])).T,
'upper']
}
[docs]class sector:
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 10/28/2020 SS 1.0 original
@DETAIL this class is used to store spherical patch for a given point group.
the class also has methods to compute the color of a direction by
computing the hue, saturation and lightness values in [0,1]. these
values can be converted to rgb for display with the well known
conversion formula.
All the methodology and equations have been taken from the paper:
Orientations – perfectly colored, G. Nolze and R. Hielscher, J. Appl. Cryst. (2016). 49, 1786–1802
'''
def __init__(self, pgsym, lauesym, supergroupsym, supergrouplauesym):
'''
AUTHOR: Saransh Singh, Lawrence Livermore national Lab, saransh1@llnl.gov
DATE: 11/11/2020 SS 1.0 original
11/12/2020 SS 1.1 added lauesym as additional input parameter
11/23/2020 SS 1.2 added supergroupsym as additional parameter
@detail: this routine initializes the data needed for reducing a
direction to the stereographic fundamental zone (standard
stereographic triangle) for the pointgroup/lauegroup symmetry
of the crystal.
'''
self.vertices = {}
self.ntriangle = {}
self.barycenter = {}
self.connectivity = {}
self.hemisphere = {}
data = pg2vertex[pgsym]
self.ntriangle['pg'] = data[0]
self.vertices['pg'] = data[1]
self.connectivity['pg'] = data[2]
self.hemisphere['pg'] = data[3]
data = pg2vertex[lauesym]
self.ntriangle['laue'] = data[0]
self.vertices['laue'] = data[1]
self.connectivity['laue'] = data[2]
self.hemisphere['laue'] = data[3]
data = pg2vertex[supergroupsym]
self.ntriangle['super'] = data[0]
self.vertices['super'] = data[1]
self.connectivity['super'] = data[2]
self.hemisphere['super'] = data[3]
data = pg2vertex[supergrouplauesym]
self.ntriangle['superlaue'] = data[0]
self.vertices['superlaue'] = data[1]
self.connectivity['superlaue'] = data[2]
self.hemisphere['superlaue'] = data[3]
if(self.ntriangle['pg'] != 0):
# compute the barycenter or the centroid of point group
b = np.mean(self.vertices['pg'], axis=1)
b = b/np.linalg.norm(b)
self.barycenter['pg'] = b
else:
self.barycenter['pg'] = np.array([0., 0., 1.])
if(self.ntriangle['laue'] != 0):
# compute the barycenter or the centroid of the laue group triangle
b = np.mean(self.vertices['laue'], axis=1)
b = b/np.linalg.norm(b)
self.barycenter['laue'] = b
else:
self.barycenter['laue'] = np.array([0., 0., 1.])
if(self.ntriangle['super'] != 0):
# compute the barycenter or the centroid of the supergroup group triangle
b = np.mean(self.vertices['super'], axis=1)
b = b/np.linalg.norm(b)
self.barycenter['super'] = b
else:
self.barycenter['super'] = np.array([0., 0., 1.])
if(self.ntriangle['superlaue'] != 0):
# compute the barycenter or the centroid of the supergroup group triangle
b = np.mean(self.vertices['superlaue'], axis=1)
b = b/np.linalg.norm(b)
self.barycenter['superlaue'] = b
else:
self.barycenter['superlaue'] = np.array([0., 0., 1.])
[docs] def check_norm(self, dir3):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 10/29/2020 SS 1.0 original
@PARAM dir3 direction in fundamental sector. size is nx3
@DETAIL this function is used to make sure the directions are all unit norm
'''
n = np.linalg.norm(dir3, axis=1)
mask = n > eps
n = n[mask]
dir3[mask, :] = dir3[mask, :]/np.tile(n, [3, 1]).T
[docs] def check_hemisphere(self):
zcoord = np.array([self.vx[2], self.vy[2], self.vz[2]])
if(np.logical_or(np.all(zcoord >= 0.), np.all(zcoord <= 0.))):
pass
else:
raise RuntimeError("sphere_sector: the vertices of the stereographic \
triangle are not in the same hemisphere")
[docs] def inside_sphericalpatch(self, vertex, dir3):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 12/09/2020 SS 1.0 original
@PARAM vertex vertices of the spherical triangle
dir3 normalized direction vectors
switch which group to check. acceptable arguments are 'pg', 'laue', 'supergroup'
and 'supergroup_laue'
@DETAIL check if direction is inside a spherical patch
the logic used as follows:
if determinant of [x A B], [x B C] and [x C A] are
all same sign, then the sphere is inside the traingle
formed by A, B and C
returns a mask with inside as True and outside as False
'''
nn = vertex.shape[1]
mask = []
d = np.zeros([nn, ])
for x in dir3:
x2 = np.atleast_2d(x).T
for ii in range(nn):
A = np.atleast_2d(vertex[:, np.mod(ii, nn)]).T
B = np.atleast_2d(vertex[:, np.mod(ii+1, nn)]).T
d[ii] = np.linalg.det(np.hstack((x2, A, B)))
'''
catching cases very close to FZ boundary when the
determinant can be very small positive or negative
number
'''
if(np.abs(d[ii]) < eps):
d[ii] = 0.
ss = np.unique(np.sign(d))
if(np.all(ss >= 0.)):
mask.append(True)
else:
mask.append(False)
mask = np.array(mask)
return mask
[docs] def fillet_region(self, dir3, switch):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 12/09/2020 SS 1.0 original
@PARAM vertex vertices of the spherical triangle
dir3 normalized direction vectors
switch which group to check. acceptable arguments are 'super', 'superlaue'
'super' ----> point group coloring
'superlaue' ----> laue group coloring
this function will check which fillet the point lies in
returns 0 if its barycenter, vertex 0 and vertex 1
returns 1 if its barycenter, vertex 1 and vertex 2
returns 2 if its barycenter, vertex 2 and vertex 3
it is implicitly assumed that the point lies inside the
spherical triangle. behavior is unknown if it is not the
case
first make the vertices of the three fillets
'''
vertex = np.copy(self.vertices[switch])
fregion = -np.ones([dir3.shape[0], ]).astype(np.int32)
bar_cen = self.barycenter[switch]
# if barycenter matches one of the vertices, then remove that vertex
mask = np.all(bar_cen == vertex.T,axis=1)
vertex = vertex[:,~mask]
nn = vertex.shape[1]
f = np.zeros([nn, 3, 3])
for i in range(nn):
idx1 = np.mod(i, nn)
idx2 = np.mod(i+1, nn)
A = np.atleast_2d(vertex[:, idx1]).T
B = np.atleast_2d(vertex[:, idx2]).T
f[i, :, :] = np.hstack((np.atleast_2d(bar_cen).T, A, B))
for i in range(nn):
inside = np.logical_and(self.inside_sphericalpatch(
np.squeeze(f[i, :, :]), dir3),
fregion == -1)
fregion[inside] = i
return fregion
[docs] def point_on_boundary(self, dir3, switch):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 12/09/2020 SS 1.0 original
@PARAM dir3 direction in fundamental sector. size is nx3
switch color using pg or laue group
@DETAIL this function figures out the equivalent point on the boundary
given that the point is inside the spherical triangle
'''
vertex = self.vertices[switch]
fregion = self.fillet_region(dir3, switch)
dir3_b = np.zeros(dir3.shape)
nn = vertex.shape[1]
for i in range(fregion.shape[0]):
f = fregion[i]
d = dir3[i, :]
A = vertex[:, np.mod(f, nn)]
B = vertex[:, np.mod(f+1, nn)]
nhat = np.cross(B, A)
nhat = nhat/np.linalg.norm(nhat)
lam = np.dot(nhat, d)
deldir = lam*nhat
dp = d - deldir
ndp = np.linalg.norm(dp)
if(ndp > 0.):
dp = dp/ndp
else:
dp = d
dir3_b[i, :] = dp
return dir3_b, fregion
[docs] def calculate_rho(self, dir3, switch):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 12/09/2020 SS 1.0 original
@PARAM dir3 direction in fundamental sector. size is nx3
switch color using pg or laue group
@DETAIL this function is used to calculate the azimuthal angle
of a bunch of directions. it is assumed all directions
are indide the SST
'''
vertex = self.vertices[switch]
bar_cen = self.barycenter[switch]
rho = np.zeros([dir3.shape[0], ])
# handle triclinic and monoclinic cases a little differently
if(np.all(bar_cen == np.array([0., 0., 1.]))):
rho = np.arctan2(dir3[:,1], dir3[:,0]) + np.pi
else:
dir3_b, fregion = self.point_on_boundary(dir3, switch)
nn = vertex.shape[1]
for i in range(fregion.shape[0]):
f = fregion[i]
d = dir3_b[i, :]
A = vertex[:, np.mod(f, nn)]
B = vertex[:, np.mod(f+1, nn)]
# angle between A and B
omega = np.dot(A, B)
if(np.abs(omega) > 1.):
omega = np.sign(omega)
# angle between point and A
omegap = np.dot(A, d)
if(np.abs(omegap) > 1.):
omegap = np.sign(omega)
omega = np.arccos(omega)
omegap = np.arccos(omegap)
if(omegap != 0.):
rho[i] = 2*np.pi*omegap/omega/nn + f*2.*np.pi/nn
else:
rho[i] = f*2.*np.pi/nn
return rho
[docs] def calculate_theta(self, dir3, switch):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 12/09/2020 SS 1.0 original
@PARAM dir3 direction in fundamental sector. size is nx3
switch color using pg or laue group
@DETAIL this function is used to calculate the polar angle
of direction vectors. it is assumed that the direction vector
lies inside the SST
'''
vertex = self.vertices[switch]
dir3_b, fregion = self.point_on_boundary(dir3, switch)
theta = np.zeros([dir3.shape[0], ])
bar_cen = self.barycenter[switch]
# handle triclinic and monoclinic cases a little differently
if(np.all(bar_cen == np.array([0., 0., 1.]))):
dp = np.dot(np.array([0., 0., 1.]), dir3.T)
# catch some cases where dot product is 1+/-epsilon
mask = np.abs(dp) > 1.
dp[mask] = np.sign(dp[mask])
theta = np.arccos(dp)
else:
# first calculate the angle the point makes with the barycenter
omega = np.dot(bar_cen, dir3.T)
mask = np.abs(omega) > 1.0
omega[mask] = np.sign(omega[mask])
# calculate the angle the boundary point makes with the barycenter
omegap = np.dot(bar_cen, dir3_b.T)
mask = np.abs(omegap) > 1.0
omegap[mask] = np.sign(omegap[mask])
omega = np.arccos(omega)
omegap = np.arccos(omegap)
zmask = omegap == 0.
theta[~zmask] = np.pi*omega[~zmask]/omegap[~zmask]/2.0
theta[zmask] = 0.0
return theta
[docs] def hue_speed(self, rho):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 12/09/2020 SS 1.0 original
@PARAM rho azimuthal angle
@DETAIL calculate the hue speed for a vector of azimuthal angles
this is utilized in increasing the area of the red, blue and
green regions
'''
rho = rho - np.pi
v = 0.5 + np.exp(-(4./7.)*rho**2) + \
np.exp(-(4./7.)*(rho - 2.*np.pi/3.)**2) + \
np.exp(-(4./7.)*(rho + 2.*np.pi/3.)**2)
return v
[docs] def hue_speed_normalization_factor(self):
pass
[docs] def calc_hue(self, dir3, switch):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 10/28/2020 SS 1.0 original
11/23/2020 SS 1.2 added mask argument which tell the directions
for which the supergroup reductions dont match the point or laue
group reductions. mask has size dir3.shape[0]
12/09/2020 SS 2.0 completely rewrite the way in which computation
is performed. all the routines have been rewritten
@PARAM dir3 direction in fundamental sector. behavior is undefined if
direction is outside the fundamental sector
switch color by laue group or point group? acceptable values are 'super', 'superlaue'
'super' ----> point group coloring
'superlaue' ----> laue group coloring
@DETAIL calculate hue. this is aggigned based on the azimuthal angle in the
stereographic triangle. the laueswitch controls which fundamental sector to use.
'''
rho = self.calculate_rho(dir3, switch)
r = np.linspace(0., 2*np.pi, 1000)
v = self.hue_speed(r)
cons = np.trapz(v, r)
h = np.zeros(rho.shape)
for i in range(rho.shape[0]):
r = np.linspace(0., rho[i], 1000)
v = self.hue_speed(r)
h[i] = np.trapz(v, r)/cons
return h
[docs] def calc_saturation(self, l):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 10/28/2020 SS 1.0 original
11/12/2020 SS 1.1 added laueswitch as argument
11/16/2020 SS 2.0 more balanced saturation from JAC papaer
the factor lambda_s is hard coded to 1/4. Since lighness is needed,
the function now uses L as an input instead of dir3 to avoid recomputing L
11/23/2020 SS 1.2 added mask argument which tell the directions
for which the supergroup reductions dont match the point or laue
group reductions. mask has size dir3.shape[0]
@PARAM L lightness values
@DETAIL calculate saturation. this is always set to 1.
'''
s = 1. - 2.*0.25*np.abs(l - 0.5)
return s
[docs] def calc_lightness(self, dir3, mask, switch):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 10/28/2020 SS 1.0 original
11/12/2020 SS 1.1 added laueswitch as argument
11/16/2020 SS 1.2 more balanced lightness key from the JAC paper
the factor lambda is hard coded to 1/4
11/23/2020 SS 1.2 added mask argument which tell the directions
for which the supergroup reductions dont match the point or laue
group reductions. mask has size dir3.shape[0]
12/09/2020 SS 2.0 completely rewrite the way in which computation
is performed. all the routines have been rewritten
@PARAM dir3 direction in fundamental sector. behavior is undefined if
laueswitch get colors in laue group or pg
mask boolean mask for points where the symmetry reductions donot match the
supergroup symmetry reductions
@DETAIL this function is used to calculate the hsl color for direction vectors
in dir3. if laueswitch is True, then color is assigned based on laue group
'''
theta = np.pi - self.calculate_theta(dir3, switch)
f1 = theta/np.pi
f2 = np.sin(theta/2.)**2
l = 0.35*f1 + 0.65*f2
l[~mask] = 1. - l[~mask]
return l
[docs] def get_color(self, dir3, mask, switch):
'''
@AUTHOR Saransh Singh, Lawrence Livermore National Lab, saransh1@llnl.gov
@DATE 10/28/2020 SS 1.0 original
11/12/2020 SS 1.1 added laueswitch as argument
11/23/2020 SS 1.2 added mask argument which tell the directions
for which the supergroup reductions dont match the point or laue
group reductions. mask has size dir3.shape[0]
@PARAM dir3 direction in fundamental sector. behavior is undefined if
mask True if symmetry reduction of dir3 using point group does not
match the super group and False otherwise
switch get colors in laue group or pg
@DETAIL this function is used to calculate the hsl color for direction vectors
in dir3. if laueswitch is True, then color is assigned based on laue group
'''
hsl = np.zeros(dir3.shape)
hsl[:, 0] = self.calc_hue(dir3, switch)
hsl[:, 2] = self.calc_lightness(dir3, mask, switch)
hsl[:, 1] = self.calc_saturation(hsl[:, 2])
return hsl