Repository navigation
Expand file tree
/
Copy pathupf.py
More file actions
3318 lines (2858 loc) · 107 KB
/
Copy pathupf.py
File metadata and controls
3318 lines (2858 loc) · 107 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""
This module started as a hobby project while learning Python back in 2011
when I was a graduate student. At the moment I am using it for my custom pole
figures from discrete orientations used for VPSC-like crystal plasticity
codes inclduing VPSC, dEVPSC
Features:
crystal symmetries : cubic and hexagonal
This module can be easily extened for other crystal symmetries.
At least, it additionally works for orthorhombic crystal structure
for a paper I worked on uranium. The crystal symmetry should be
extended and generalized for all crystal symmetries existing, which
has not been pursued yet.
Pole figure and inverse pole figure plotting by stereographic
projection. Both contour and dot types are available for pole
figures, whereas only dot type is available for inverse pole figure, yet.
It should be machine independent. Make the way treating the data
uniform and intiutive and simple.
--------
examples
--------
>>> import upf
>>> mypf = upf.polefigure(ngrain=8000,
# or filename ='texture/08000.cmb'
csym = 'cubic', cdim=[1.,1.,1.],
cang =[90.,90.,90.])
>>> cnts = mypf.pf(pole=[ [1,0,0],[1,1,0],[1,1,1]],
mode='contourf', ifig=2,
dm=7.5, dn=7.5, levels=None, cmode='gray_r')
* Pole figure plotting upon the polycrystal aggregate.
--> refer to 'class polefigure'
* Experimental pole figure plotting software where
the range of khi (tiliting angle) is often limited.
However, the plotting scheme will be very similiar to the
pole figure for polycrystalline aggregates, in that
experimental pole figure often comes as a grid set of data
in the polar coordinate system (theta as angle, and khi as
radius)
--> First of all, I should come up with a uniform format
having a grid format consisting of theta and khi axes.
the resolution of grid should be decided after getting the
data.
range of data:
theta : 0. ~ 2.*pi
khi : 0. ~ pi/2.
detecting the np.shape and decide the incremental resolution
of each axis.
Let's say it will be something like below:
>>> m, n = np.shape(data)
# m is the number of grid along theta axis
# while n is the number of grid along khi
--> Often, as far as I know, experimental pole figure should be
normalized since the incremental scanning area is varying with
increasing khi(tilting angle). The issues related to background
and defocus effect should be dealt with independently, as well.
The format controller is also necessary.
Current cases of interests:
1. Bruker D8 Discovery (at GIFT POSTECH) (UXD)
2. Unknown system at Dr. Steglich's institute
3. Experimental PF (popLA format)
>>> import upf
>>> mypf = upf.polefigure(epf='???.epf')
..
..
>>> cnts = mypf.pf()
"""
# print __doc__
"""
Updates logs are no longer tracked as the project is version-controlled using Git.
# 1
2011-16-Mar
Contour plot is done on the given axes if necessary.
# 2
2011-17-April
The symmetry module is cythonized.
The __equiv__ method has been taken out from the loop over grains,
which saves some computational waste.
# 3
2011-18-April
The hardwired smoothing rim is expanded to the 2nd rim.
Thus now it is n=0, n=1 axes along which the intensities are averaged.
# 4
2011-14-Sept
The pixel view superimposition to contour pole figure is initiated.
Cython part having been used for 'sym.py' has been removed.
"""
#----------------------------------------------------------------------c
## import library blocks
import warnings
# warnings.filterwarnings("ignore")
# warnings.filterwarnings("error")
import numpy as np
import matplotlib.pyplot as plt
import matplotlib #matplotlib as raw
import os, glob, math
from .randomEuler import randomEuler as re
from .euler import euler # in euler module def euler:
from .euler import eulers
# A-matrix and Euler angles
import time,random
import fortranformat as ff
import sys
try:
import MP
except:
print('-'*50)
print('MP was not installed')
print('You may clone it and install via:')
print('git@github.com:youngung/mpl-lib.git')
print('-'*50)
else:
from MP import progress_bar
t2s = progress_bar.convert_sec_to_string
uet = progress_bar.update_elapsed_time
## removing joblib
is_joblib=False
pi = math.pi;
cos = math.cos; sin = math.sin
# import pp ## parallel
def cub(filename=None,gr=None,ifig=3,**kwargs):
"""
arguments
=========
filename = None
gr = None
ifig = 3
pole = [[1,0,0],[1,1,0],[1,1,1]],
cmode = None):
"""
if filename!=None: mypf = polefigure(filename=filename, csym='cubic')
elif gr!=None: mypf = polefigure(grains=gr, csym='cubic')
fig=mypf.pf_new(**kwargs)
return fig
def cubgr(gr=None,ifig=3,poles=[[1,0,0],[1,1,0],[1,1,1]]):
"""
Arguments
=========
gr
ifig
poles
"""
mypf = polefigure(grains=gr, csym='cubic')
fig = mypf.pf_new(poles=poles,cmap='jet')
return fig
def pfnorm(data):
"""
experimental incomplete pole figure preliminary normalization
data format should be correct
grid (phi, khi)
resolution should be 5.0 degress in both phi and khi
phi range: 0~ 355
khi range: 0~ 5*(nn-1)
Argument
========
data
"""
# All angles are in radian
if len(data)!=72:
print('number of phi grid: %i'%len(data))
raise IOError('Unexpected resolution along phi axis')
dphi = 360. / len(data)
dphi = dphi * np.pi / 180. # dphi
dkhi = 5. * np.pi / 180. # dkhi
print('dkhi, dphi', dphi*180./np.pi, dkhi*180./np.pi)
nkhi = len(data[0])
phi_i = 0.
phi_f = np.pi * 2.
khi_i = 0.
khi_f = dkhi * (nkhi - 1)
print('khi range', khi_i, khi_f*180./np.pi)
print('phi range', phi_i, phi_f*180./np.pi)
# spanned area, i.e., the area of incomplete hemisphere:
# area = (np.cos(khi_f) - np.cos(khi_i)) * (phi_f - phi_i)
Nf = 0.
a = 0.
b = 0.
## below needs vectorization
for i in range(len(data)):
for j in range(len(data[0])):
a = a + np.sin(dkhi*j)
for i in range(len(data)):
for j in range(len(data[i])):
b = b + data[i,j] * np.sin(dkhi*j)
# for k in xrange(len(data)):
# for l in xrange(len(data[k])):
# b = b + data[k, l] * np.sin(dkhi*l)
for i in range(len(data)): #phi
for j in range(len(data[i])): #khi
data[i,j] = data[i,j] * a / b
return data
def epfformat(mode=None, filename=None):
"""
Experimental pole figure format controller
mode:
"steglich"
"bruker" *.uxd file
"epf" (2011-Oct-6) epf popLA experimental pole figure format
"xpc" (2017-Feb) xcp format compliant with MAUD
Returns the pole figure data as
the standard format (m x n numpy array)
each of axes stands for rotating (phi) and tilting (khi)
angle in the laboratory space.
These angles will be angle and radius in the
space onto which a pole figure is projected.
conventions:
tilting angle: khi
rotating angle: phi
dk: incremental khi angle
dp: incremental phi angle
nk: number of points along khi axis
np: number of points along phi axis
angles are converted into radian whenever possible
Arguments
=========
mode = None
filename = None
Returns
=======
data
max_khi
hkl
"""
## steglich's format
nskip_calc = 13
nskip_raw = 28
##
if mode=='steglich':
# Calculated or raw pole figure
i = 0
print('filename=', filename)
if os.path.isfile(filename)!=True:
raise IOError('file is not available')
while True:
try:
data = np.loadtxt(
filename, skiprows=i)
except: i = i + 1
else:
print('number of skipped rows: %i'%i)
break
if i>1000: raise IOError('something is wrong')
## raw pole figure format
if i==nskip_raw:
# axes: (phi, khi)
data = data.T # (khi, phi)
# upon each axis
f = open(filename)
temp = f.readlines()
khi = list(map(float, temp[nskip_raw - 1].split()[1:]))
khi = np.array(khi)
khi = khi * np.pi/ 180.
phi = data[0] #first column is for phi
phi = phi * np.pi/ 180.
data = data[1:] #remove phi
data = data.T # (phi, khi) axis
## dp and dk
dp = phi[1] - phi[0]
dk = khi[1] - khi[0]
## shift phi back-modification
phi, data = shiftphi(data, dp, phi)
## shift phi back-modification ends
## check if there is phi=0, 360 at the same time
## if that's the case remove data along phi=360
phi, data = duplicate0_360(phi, data)
##
## check if this is incomplete pole figure
tiny = 0.000001
isincomplete=False
if np.pi/2. - khi[-1] > tiny:
print('Incomplete pole figure')
print('khi range: %3.2f ~%3.2f'%(
khi[0]*180./np.pi, khi[-1]*180./np.pi))
isincomplete=True
## normalization
if input('y(norm), or n(no)>>>')=='y':
data = pfnorm(data)
dum = np.zeros((np.pi*2./dp, np.pi/2./dk+1))
for i in range(len(data)):
for j in range(len(data[i])):
dum[i, j] = data[i, j]
data = dum.copy()
del dum
## calculated pole figure format
elif i==nskip_calc: #He had two formats: raw and calculated.
print('Calculated pole figure format')
# axes: (phi, khi)
data = data.T #(khi, phi)
f = open(filename)
temp = f.readlines()
khi = list(map(float, temp[nskip_calc - 1].split()[1:]))
khi = np.array(khi)
khi = khi * np.pi / 180.
phi = data[0]
phi = phi * np.pi / 180.
data = data[1:]
data = data.T #(phi, khi)
## dp and dk
dp = phi[1] - phi[0]
dk = khi[1] - khi[0]
## shift phi back-modification
phi, data = shiftphi(data, dp, phi)
##
## check if there is phi=0, 360 at the same time
phi, data = duplicate0_360(phi, data)
##
elif mode=='bruker':
## make full use of existing uxd.py script
## --> normalization is missing...
## This must be completed!
from . import uxd
print('You are now in the bruker mode under epfformat')
print('given file name is %s'%filename)
myuxd = uxd.pf(filename=filename, mode='pf')
if len(myuxd.polefigures)>1:
print('multiple pole figures are found')
input()
for i in range(len(myuxd.polefigures)):
pf = myuxd.polefigures[i]
pf = pfnorm(pf) ##normalize
elif mode=='epf':
"""
ready made popLA epf format parser
consider the possibility of multiple number of polefigure
## phi must be 0~355, khi must be 0~90 with 5 as an ang
resolution
"""
print('You are now reading experimental pole figure(s) :%s'%filename)
blocks = open(filename, 'rU').read().split('(')[1:]
if len(blocks)==0:
msg1 = 'epf parser in upf assumes that hkl is embraced by paratheses'
msg2 = ' %s has no paranthesis that embraces hkl was found'%filename
msg = '%s \n %s'%(msg1,msg2)
print('-'*52)
print(msg)
#raise IOError, msg
print('upf will keep proceding with using upf.parse_epf method with n_unit=79')
print('-'*52)
blocks = parse_epf(filename)
npf = len(blocks)
if npf==0: raise IOError('No pf block found.')
datasets = []; max_khi = []
if npf>1: hkls=[] ## multiple number of pole figures in a file
for i in range(npf):
#d = __epffiletrimmer__(blocks[i]) #only for popLA epf format
## epffiletrimmer should return a block of intensity grid map
## as well as maximum khi.
## --> modification (2011 NOV 8)
hkl = blocks[i][0:3] #hkl
hkl = list(map(int, [hkl[0],hkl[1],hkl[2]]))
if npf>1: hkls.append(hkl)
if blocks[i][3]!=')':
print('Caution: unexpected hkl labeling format')
d, mxk = __epffiletrimmer__(blocks[i])
#only for popLA epf format
datasets.append(d)
max_khi.append(mxk)
print("number of pole figures:", len(datasets))
## presumption of using 19x72 should be deprecated...
data = np.zeros((len(datasets), 19, 72)) #npf, nphi, nkhi
dum = data.copy()
for i in range(len(datasets)):
for j in range(len(datasets[i])):
for k in range(len(datasets[i][j])):
data[i,j,k] = datasets[i][j][k]
## Swap the axis
data = data.swapaxes(1,2) # from 72 x 19 to 19 x 72
## psuedo-normalization
for i in range(len(data)):
data[i] = pfnorm(data[i].copy())
## --
if npf>1: hkl=hkls[::]
else: hkl=[hkl]
return data, max_khi, hkl
elif mode=='xpc':
"""
Adapted .xpc format parser, from
https://github.com/usnistgov/texture
commit 9c0ac85
based on ready made popLA epf format parser
"""
import pandas as pd
print('You are now reading experimental pole figure(s) :%s'%filename)
blocks = open(filename, 'rU').read().split('\n\n\n\n')[1:]
print('There are %s blocks of data found'%len(blocks))
if len(blocks)==0:
msg1 = 'xpc parser in upf assumes that pole figures are separated by 4 new lines'
msg2 = ' searching %s finds no set of 4 new lines in '%filename
msg = '%s \n %s'%(msg1,msg2)
raise IOError(msg)
# blocks = parse_epf(filename)
npf = len(blocks)
if npf==0: raise IOError('No pf block found.')
datasets = []; max_khi = []
if npf>1: hkls=["HKL"] ## multiple number of pole figures in a file
for part in blocks:
line=part.split('\n')
# print len(line)
#header lines
structureline=ff.FortranRecordReader('(6f10.4,1x,i4,1x,i4)') # the head-line in each data block
[a,b,c,alpha,beta,gamma,crystalclass,something] \
=structureline.read(line[1])
pfDefline=ff.FortranRecordReader('(1x,3i3,6f5.1,2i2)')
[h,k,l,unknown1,tilt,tiltinc,unknown2,rotation,\
rotationinc,unknown3,unknown4]=pfDefline.read(line[2])
#for the rest of the lines, do the following
dataline=ff.FortranRecordReader('(1x,18i4)')
# Pretty ugly code, but works...
grouping=[[3,4,5,6],[7,8,9,10],[11,12,13,14],[15,16,17,18],[19,20,21,22],[23,24,25,26], \
[27,28,29,30],[31,32,33,34],[35,36,37,38],[39,40,41,42],[43,44,45,46],[47,48,49,50], \
[51,52,53,54],[55,56,57,58],[59,60,61,62],[63,64,65,66],[67,68,69,70],[71,72,73,74], \
[75,76,77,78]]
#dataset=np.array([])
dataset=[]
for item in grouping:
#print item[0],item[1],item[2],item[3]
parsed=dataline.read(line[item[0]])
parsed.extend(dataline.read(line[item[1]]))
parsed.extend(dataline.read(line[item[2]]))
parsed.extend(dataline.read(line[item[3]]))
dataset.append(parsed)
#print dataset
#now saves as a dataframe, and wraps 360 to 0 degrees
#row and column indexes are by degrees
df=pd.DataFrame(dataset, index=np.arange(0,91,5))
df.columns=[np.arange(0,360,5)]
df[360]=df.ix[:,0]
#print df.ix[:,0]
#df["Tilt Angle"] =np.arange(0,92,5)
#df.index.names=[np.transpose(np.arange(0,90,5))]
#print df
hkl = [h,k,l] #hkl
#hkl = map(int, [hkl[0],hkl[1],hkl[2]])
#print hkl
hkls.append(hkl)
datasets.append(df)
#max_khi.append(mxk)
#print hkls
print("number of pole figures:", len(datasets))
### convert the panda data frame to numpy
npf=len(datasets)
datasets_compliant=np.zeros((npf,72,19))
for i in range(npf):
array = datasets[i].values
arrayt = array.T
## ignore the last phi axis as it is repeated (phi360=phi0)
datasets_compliant[i,:,:]=arrayt[:72,:]
return datasets_compliant, hkls
else: raise IOError('Unexpected mode is given')
def __epffiletrimmer__(block):
"""
EPF (experimental pole figure) trimmer for a single block of data
EPF: The popLA format
Arguments
=========
block
"""
pfdata = block.split('\n')
dkhi = float(pfdata[0][5:9]) #khi incremental angle
fkhi = float(pfdata[0][9:14]) #khi end angle
dphi = float(pfdata[0][14:19]) #phi incremental angle
# written as 360 but it is only upto 355
fphi = float(pfdata[0][19:24]) - dphi
## now it is enfored to have 5 deg resolution, which
## results in 19x72
## intensities along khi and phi axes coordinate
data = np.zeros((19,72))
## the rest information is neglected from the header
pfdata = pfdata[1:]
lines = []
for i in range(len(pfdata)):
if len(pfdata[i])==72:
lines.append(pfdata[i][:])
elif len(pfdata[i])==73:
lines.append(pfdata[i][1:])
pfdata = lines # (maxkhi/dkhi + 1) * 4
if len(pfdata)!=76:# 76 = ((90 / 5) + 1) * 4
# (4lines for one khi level)
print('len(pfdata) =', len(pfdata))
print(pfdata)
raise IOError('Unexpected pfdata format or type')
if True:
for j in range(19): #90 / 5 + 1 #number of khi threads
kh = pfdata[j*4: (j+1)*4] #along the same kh level
khline = ''
for k in range(4):
khline = khline + kh[k]
khline # all data in a thread of
# string every 4digits corresponds to a datum
kp = 0 # each point along phi
for k in range(72): #72 = 288 / 4 (4digits) : 0~355 5.0
datum = khline[k*4:(k+1)*4]
data[j,k] = int(datum)
# block of mesh grid pole figure map and khi final angle.
return data, fkhi
def shiftphi(data, dp, phi):
"""
shifted phi modification
Arguments
=========
data
dp
phi
"""
tiny = 0.0000001
if abs(phi[0] - 0.)> tiny and abs((dp-phi[0])-dp/2.)< tiny:
dum = data.copy()
for i in range(len(data)): #phi axis
for j in range(len(data[i])): #khi axis
dum[i, j] = (data[i-1, j] + data[i, j]) / 2.
data = dum.copy()
return phi, data
def duplicate0_360(phi, data):
"""
If both phi=0 and phi=360 exist,
delete phi=360
Arguments
=========
phi
data
"""
tiny = 0.00000000001
isallsame = True
for i in range(len(data[0])):
print(data[0,i])
print(data[-1,i])
if any(abs(data[0,i]-data[-1,i])>tiny for i in range(len(data[0]))):
print(data[0,i])
print(data[-1,i])
isallsame = False
isphioverlapped=False
print(abs(phi[0] - phi[-1] - 2. * np.pi))
if abs(phi[0] - phi[-1] + 2. * np.pi)<tiny:
isphioverlapped=True
if isphioverlapped and isallsame:
newdata = np.zeros((data.shape[0]-1, data.shape[1]))
for i in range(len(newdata)):
for j in range(len(newdata[i])):
newdata[i,j] = data[i,j]
return phi[:-1], newdata
elif isphioverlapped and isallsame==False:
print("conflict results!")
print("phi=0, and 360 turned out to coexist but")
print("Values along these two axes are not equivalent")
raise IOError
elif not(isphioverlapped) and isallsame:
print("Conflict results!")
print("Phi is overlapped but phi[0] and phi[-1] is same")
raise IOError
else:
print("No duplicated axis is found")
return phi, data
def pole2f(poles_sa,poles_wgt,dth,dph,f):
"""
Collect weights belonging to each 'sector' in the polar coordinate
of pole figure projection.
Arguments
---------
poles_sa: pole referenced to sample axes
poles_wgt: weight of each pole (originated from the weight of discrete orientations)
dth: incremental rotation angle ( -180 ~ 180)
dph: incremental 'tilting' angle ( 0 ~ 180)
"""
tiny = 1e-9
for i, pole_sa in enumerate(poles_sa):
theta,phi = cart2sph(pole_sa)
ix = int((theta*180./np.pi+180)/dth-tiny)
iy = int( (phi*180./np.pi) /dph-tiny)
f[ix,iy]=f[ix,iy]+poles_wgt[i]
return f
def pole2f_cols(poles_sa,poles_wgt,poles_col,f,dth,dph,fcol):
"""
Collect weights belonging to each 'sector' in the polar coordinate
of pole figure projection.
Arguments
---------
poles_sa: pole referenced to sample axes
poles_wgt: weight of each pole (originated from the weight of discrete orientations)
poles_col: extra column quantities
f
dth: incremental rotation angle ( -180 ~ 180)
dph: incremental 'tilting' angle ( 0 ~ 180)
f_ij^{a}: weighted value of a scalar quantity {a}, belonging to each sector (ij).
"""
tiny = 1e-9
ncol=poles_col.shape[-1]
for i, pole_sa in enumerate(poles_sa):
theta,phi = cart2sph(pole_sa)#poles_sa[i])
ix = int((theta*180./np.pi+180)/dth-tiny)
iy = int( (phi*180./np.pi) /dph-tiny)
for icol in range(ncol):
fcol[ix,iy,icol]=fcol[ix,iy,icol]+poles_col[i,icol]*poles_wgt[i]/f[ix,iy]
return fcol
def cart2polar(x,y):
"""
cartesian to polar coordinate
Arguments
=========
x
y
"""
r = np.sqrt(x**2+y**2)
theta = np.arctan2(y,x)
return r, theta
def circle(center=[0,0], r=1.):
"""
Draw a circle around the given center point with
a radius of r(as given)
The default settings are as below.
Arugments
=========
center = [0,0]
r = 1.
"""
ang = np.linspace(0,2*np.pi,1000)
#unit circle * radius
x = np.cos(ang)*r
y = np.sin(ang)*r
#circle transloation
x = x + center[0]
y = y + center[0]
return x,y
def calc_vref_and_rot(a,b,csym,cdim,cang,nang=100):
from .sym import cv
icsym=get_icsym(csym)
aca=cv(miller=a,icsym=icsym,cdim=cdim,cang=cang)
bca=cv(miller=b,icsym=icsym,cdim=cdim,cang=cang)
vref=np.cross(aca,bca)
##
thf=np.arccos(np.dot(aca,bca))
ths=np.linspace(0,thf,nang)
rots=np.zeros((nang,3,3))
varc=np.zeros((nang,3))
for i, th in enumerate(ths):
rots[i,:,:]=vector_ang(vref,np.rad2deg(th))
return aca, bca, thf, vref, rots
# def get_ipf_boundary(a,b,c,nres,csym,cdim,cang):
# pairs=[[a,b],[b,c],[c,a]]
# coords=np.zeros((2,(nres-1)*3+1))
# for i, pair in enumerate(pairs[:3]):
# aca,bca,thf,vref,rots=calc_vref_and_rot(*pair,csym,cdim,cang,nres)
# varc=calc_arc(aca,rots)
# xy=np.zeros((len(varc),2))
# for j, point in enumerate(varc):
# xy[j,:]=projection(point)
# i0=i*(nres-1)
# i1=i0+nres-1
# coords[:,i0:i1]=xy[0:-1,:].T
# coords[:,-1]=coords[:,0]
# return coords
def calc_arc(aca,rots):
"""
Arguments
---------
aca crystal direction
rots: rotation matrix
"""
nang=rots.shape[0]
v_arc=np.zeros((nang,3))
for i,rot in enumerate(rots):
v_arc[i,:]=np.dot(rot,aca)
return v_arc
def basic_triangle():
"""
provide the boundary of basic triangle
"""
v100_ca = np.array([1,0,0])
v110_ca = np.array([1,1,0]) / np.sqrt(2.)
v111_ca = np.array([1,1,1]) / np.sqrt(3.)
delt_thet_100_110 = np.pi / 2.0
delt_thet_111_110 =\
np.arccos(np.dot(v110_ca, v111_ca))
delt_thet_100_111 = \
np.arccos(np.dot(v100_ca, v111_ca))
line1 # 100_110
line2 # 110_111
line3 # 111_100
def trace(thet_f, n, v1, v2):
"""
Trance of vectors that runs on the plane made
by the two given vectors
---------
Arguments
---------
thet_f (in Radian)
n
v1
v2
"""
v1 = v1 / np.sqrt(v1[0]**2 + v1[1]**2 + v1[2]**2)
v2 = v2 / np.sqrt(v2[0]**2 + v2[1]**2 + v2[2]**2)
v3 = np.cross(v1, v2)
v3 = v3 / np.sqrt(v3[0]**2 + v3[1]**2 + v3[2]**2)
dthet = np.linspace(0., thet_f, n)
# around v3 rotate v1 by incremental angle upto thet_f
# rotate v1 about v3 axis.
trace = []
for iang in range(n):
th = dthet[iang]
r = vector_ang(v3, th)
vnew = np.dot(r, v1)
trace.append(vnew)
return trace
def xytrace(thet_f, n, v1, v2):
"""
Returned projected trace
"""
poles = trace(thet_f, n, v1, v2)
X, Y = [], []
for i in range(len(poles)):
x,y = projection(poles[i], agrain=[0,0,0,1])
X.append(x)
Y.append(y)
return X, Y
def vector_ang(u, th):
"""
transformation matrix that rotates a vector about an axis u
by the angle, th.
---------
Arguments
---------
u
th (in Radian)
"""
pi = np.pi
idx = np.zeros((3,3))
r = np.zeros((3,3))
for i in range(3):
idx[i,i] = 1.
ct = np.cos(th)
st = np.sin(th)
cm = crossop(u)
for i in range(3):
for j in range(3):
r[i,j] = idx[i,j] * ct + st * cm[i,j] +\
(1 - ct) * u[i] * u[j]
return r
def crossop(u):
m = np.zeros((3,3))
m[0,1] = -u[2]
m[0,2] = u[1]
m[1,0] = u[2]
m[1,2] = -u[0]
m[2,0] = -u[1]
m[2,1] = u[0]
return m
def __isunique__(a, b):
"""
Is a(3) in b(m, 3)
Arguments
=========
a
b
"""
for i in range(len(b)):
## is a//b[i]? either + or -
diff0 = abs(b[i][0] - a[0]) + \
abs(b[i][1] - a[1]) + abs(b[i][2] - a[2])
diff1 = abs(b[i][0] + a[0]) + abs(b[i][1] + a[1])\
+ abs(b[i][2] + a[2])
if diff0 < 0.1**4 or diff1 < 0.1**4:
return False
return True
def __circle__(center=[0,0], r=1.):
"""
Draw a circle around the given center point with a
radius of r(as given)
The default settings are as below.
Arugments
=========
center = [0,0]
r = 1.
"""
ang = np.linspace(0,2*np.pi,1000)
#unit circle * radius
x = np.cos(ang)*r
y = np.sin(ang)*r
#circle transloation
x = x + center[0]
y = y + center[0]
return x,y
def deco_pf(ax,proj,triangle,cnt=None,miller=[0,0,0],
iopt=0,iskip_last=False,
ix='1',iy='2',mode='line',ires=True,
nArray=None,levels=None,xcoord=None,ycoord=None,ilev=0,**kwargs_ipf):
"""
Decorate matplotlib.pyplot.axes used for plotting
(inverse) pole figures
iopt==1: skip level lines
Arguments
---------
ax (matplotlib axes)
proj ('pf' or 'ipf')
triangle (* triangle boundary for inverse pole figure)
cnt (contour object generated by ax.contour)
miller (miller indices)
iopt
iskip_last (whether or not to skip the maximum
iso-contour line or level)
ix (xlabel, i.e., horizontal axis label)
iy (ylabel, i.e., vertial axis label)
mode pole figure plotting mode (line or fill)
ires (logial) if ires true and mode is 'line'
draw black dots on the background.
nArray
levels
xcoord
ycoord
ilev (0) 0: 0: do not draw level lines 1: draw level lines
**kwargs_ipf
"""
from .sym import calc_cvec
## ------------------------------------------
## in case if inverse pole figures, obtain
## the size of triangle.
if proj=='ipf':
y1=triangle[1].max()
y0=triangle[1].min()
x1=triangle[1].max()
x0=triangle[1].min()
yscale=y1-y0
xscale=x1-x0
#--------------------------------------------
## fontsize of appended text to pole figures will be 4*fact
fact = 1.8
# --
if mode in ['fill','line']:
clev = cnt._levels
if mode =='fill':
colors=cnt.get_facecolor()
elif mode=='line':
colors=cnt.get_edgecolor()
nlev=len(colors)
if iskip_last: nlev = nlev-1
#--------------------------------------------
## place colorbar of contours with its intensities
if iopt==1: pass
elif iopt==0 and mode in ['fill','line']:
dy=0.3
if proj=='pf':
x=[1.6,1.7]
elif proj=='ipf':
x0=triangle[0].max()+0.02
x1=x0+0.07
x=[x0,x1]
for i in range(nlev):
cc=colors[i][:3]
# # try:
# # cc = tcolors[i][0][0:3]
# # except:
# try:
# cc=cnt.get_facecolor()[i][:3]
# except: