Mechanics of Defect Evolution Library
Loading...
Searching...
No Matches
create_polycrystal_figure.py
Go to the documentation of this file.
1"""Figure 1.1: the same ten grains with and without coplanar triangle edges."""
2from pathlib import Path
3from collections import defaultdict
4from itertools import combinations
5import numpy as np
6import matplotlib
7matplotlib.use('Agg')
8import matplotlib.pyplot as plt
9from mpl_toolkits.mplot3d.art3d import Poly3DCollection, Line3DCollection
10root=Path(__file__).resolve().parents[1]
11source=root.parent/'Library/Meshes/polycrystalCube_10_periodicX.msh'
12s=source.read_text().splitlines();k=s.index('$Nodes');n=int(s[k+1])
13points={int(t[0]):np.array(t[1:4],float) for t in (x.split() for x in s[k+2:k+2+n])}
14k=s.index('$Elements');grains=defaultdict(list)
15for line in s[k+2:k+2+int(s[k+1])]:
16 a=list(map(int,line.split()))
17 if a[1] in (4,11):grains[a[3]].append(a[3+a[2]:3+a[2]+4])
18assert len(grains)==10
19allp=np.array(list(points.values()));center=(allp.max(0)+allp.min(0))/2;scale=np.max(np.ptp(allp,axis=0))
20points={k:(p-center)/scale for k,p in points.items()}
21plt.rcParams.update({'font.family':'DejaVu Sans','font.size':11,'pdf.fonttype':42})
22fig=plt.figure(figsize=(10,4.8));colors=plt.get_cmap('tab10')
23for panel,title in enumerate(['Simplicial mesh: volume triangulation','Polyhedral mesh: grains and grain boundaries']):
24 ax=fig.add_subplot(1,2,panel+1,projection='3d')
25 polygons=[]; facecolors=[]
26 for j,(gid,tets) in enumerate(sorted(grains.items())):
27 faces=defaultdict(int)
28 for tet in tets:
29 for f in combinations(tet,3):faces[tuple(sorted(f))]+=1
30 boundary=[f for f,count in faces.items() if count==1]
31 ids=set(v for f in boundary for v in f)
32 shift=.22*np.mean([points[v] for v in ids],axis=0)
33 tris=np.array([[points[v]+shift for v in f] for f in boundary])
34 color=colors(j)
35 if panel==0:
36 polygons.extend(tris);facecolors.extend([color]*len(tris))
37 else:
38 planes=defaultdict(list)
39 for f in boundary:
40 p=np.array([points[v] for v in f]);normal=np.cross(p[1]-p[0],p[2]-p[0]);normal/=np.linalg.norm(normal)
41 if normal[np.argmax(np.abs(normal))]<0:normal=-normal
42 key=tuple(np.round(np.r_[normal,np.dot(normal,p[0])],6));planes[key].append(f)
43 for fs in planes.values():
44 counts=defaultdict(int)
45 for f in fs:
46 for e in combinations(f,2):counts[tuple(sorted(e))]+=1
47 adjacency=defaultdict(list)
48 for (a,b),count in counts.items():
49 if count==1:adjacency[a].append(b);adjacency[b].append(a)
50 assert all(len(v)==2 for v in adjacency.values())
51 first=next(iter(adjacency));loop=[first];prev=None;cur=first
52 while True:
53 nxt=next(v for v in adjacency[cur] if v!=prev)
54 if nxt==first:break
55 loop.append(nxt);prev,cur=cur,nxt
56 assert len(loop)==len(adjacency)
57 polygons.append([points[v]+shift for v in loop]);facecolors.append(color)
58 ax.add_collection3d(Poly3DCollection(polygons,facecolors=facecolors,edgecolors='#344450',linewidths=.24 if panel==0 else .65,alpha=1,zsort='average'))
59 ax.set(xlim=(-.67,.67),ylim=(-.67,.67),zlim=(-.67,.67),title=title)
60 ax.set_box_aspect((1,1,1),zoom=1.5);ax.view_init(22,-55);ax.set_proj_type('ortho');ax.set_axis_off()
61fig.subplots_adjust(left=0,right=1,top=.9,bottom=.12,wspace=.02)
62fig.text(.5,.055,'10 grains • Matching colors identify the same grain • Grains separated for visibility',ha='center',fontsize=10,color='#203547')
63for ext in ['pdf','png']:fig.savefig(root/'figures'/('polycrystal_mesh_views.'+ext),dpi=180,bbox_inches='tight')
64print('Rendered',len(grains),'grains and',sum(map(len,grains.values())),'tetrahedra from',source.name)