1"""Reproducible manual illustrations; analytical curves are not simulation results."""
2from pathlib
import Path
6import matplotlib.pyplot
as plt
7from matplotlib.patches
import FancyBboxPatch, FancyArrowPatch, Polygon
8from mpl_toolkits.mplot3d.art3d
import Line3DCollection
9ROOT=Path(__file__).resolve().parents[1]
11OUT.mkdir(exist_ok=
True)
12plt.rcParams.update({
'font.size':11,
'font.family':
'DejaVu Sans',
'axes.spines.top':
False,
'axes.spines.right':
False,
'pdf.fonttype':42})
13blue=
'#176b91'; orange=
'#d27a30'; dark=
'#203547'; pale=
'#edf4f8'
15 fig.savefig(OUT/(name+
'.pdf'),bbox_inches=
'tight')
16 fig.savefig(OUT/(name+
'.png'),dpi=150,bbox_inches=
'tight')
18def box(ax,x,y,w,h,text,color=pale):
19 ax.add_patch(FancyBboxPatch((x,y),w,h,boxstyle=
'round,pad=0.02',fc=color,ec=blue,lw=1.3))
20 ax.text(x+w/2,y+h/2,text,ha=
'center',va=
'center',fontsize=10,color=dark)
22 ax.add_patch(FancyArrowPatch(a,b,arrowstyle=
'-|>',mutation_scale=13,color=blue,lw=1.5))
24lines=(ROOT.parent/
'Library/Meshes/unitCube24.msh').read_text().splitlines()
25k=lines.index(
'$Nodes'); nn=int(lines[k+1]); pts={int(s.split()[0]):np.array(list(map(float,s.split()[1:4])))
for s
in lines[k+2:k+2+nn]}
26k=lines.index(
'$Elements'); ne=int(lines[k+1]); tets=[]
27for s
in lines[k+2:k+2+ne]:
28 a=list(map(int,s.split()))
29 if a[1]==4:tets.append(a[3+a[2]:])
33 for j
in range(i):edges.add(tuple(sorted((tet[i],tet[j]))))
34fig=plt.figure(figsize=(10,4.3))
35for idx,title
in enumerate([
'Simplicial volume: 24 tetrahedra',
'Polyhedral boundary: cube faces']):
36 ax=fig.add_subplot(1,2,idx+1,projection=
'3d')
37 if idx==0: segs=[[pts[a],pts[b]]
for a,b
in edges]
39 corners=[v
for v
in pts.values()
if np.all(np.abs(v)==.5)]
40 segs=[[a,b]
for i,a
in enumerate(corners)
for b
in corners[:i]
if np.sum(a!=b)==1]
41 ax.add_collection3d(Line3DCollection(segs,colors=blue,linewidths=.8
if idx==0
else 2))
42 ax.set(xlim=(-.55,.55),ylim=(-.55,.55),zlim=(-.55,.55),title=title)
43 ax.set_box_aspect((1,1,1));ax.set_axis_off();ax.view_init(22,32)
44fig.text(.5,.02,
'Same domain; volume interpolation and boundary traversal use different representations.',ha=
'center',fontsize=10)
46fig,ax=plt.subplots(figsize=(10,4));ax.axis(
'off');ax.set(xlim=(0,10),ylim=(0,4))
47box(ax,.1,2.7,2.6,.9,
'Material file\nscales, crystal type, laws')
48box(ax,3.65,2.7,2.7,.9,
'Polycrystal\nshared material + grains')
49box(ax,7.2,2.7,2.6,.9,
'Mesh regions\ngeometric partition')
50arrow(ax,(2.7,3.15),(3.65,3.15));
arrow(ax,(7.2,3.15),(6.35,3.15))
51for x,txt
in [(1.4,
'Grain 1: C2G1\nlattice + slip systems'),(6,
'Grain 2: C2G2\nlattice + slip systems')]:
box(ax,x,.7,2.7,1,txt)
52arrow(ax,(4.4,2.7),(2.75,1.7));
arrow(ax,(5.6,2.7),(7.35,1.7));
arrow(ax,(4.1,1.2),(6,1.2));ax.text(5.05,.55,
'grain boundary',ha=
'center',fontsize=9)
53save(fig,
'polycrystal')
54fig,(ax,bx)=plt.subplots(1,2,figsize=(10,3.6));x=np.linspace(0,1,300)
55for y,l,c
in [(2*(x-.5)*(x-1),
'N₀',blue),(4*x*(1-x),
'N₁/₂',orange),(2*x*(x-.5),
'N₁',
'#668f52')]:ax.plot(x,y,label=l,color=c)
56ax.set(title=
'Quadratic Lagrange edge basis',xlabel=
'Edge coordinate',ylabel=
'Shape function');ax.legend(frameon=
False);ax.grid(alpha=.15)
57bx.set(xlim=(0,5),ylim=(0,4));bx.axis(
'off')
58for y,t
in [(3,
'Mesh + LagrangeElement'),(1.8,
'TrialFunction + weak forms'),(.6,
'WeakProblem: constraints + solve')]:
box(bx,.2,y,4.6,.7,t)
59arrow(bx,(2.5,3),(2.5,2.5));
arrow(bx,(2.5,1.8),(2.5,1.3));fig.tight_layout();
save(fig,
'fem')
60fig,ax=plt.subplots(figsize=(10,4.2));ax.set(xlim=(0,10),ylim=(0,4.5));ax.axis(
'off')
61box(ax,.3,3.4,9.4,.7,
'DefectiveCrystalBase: mesh, polycrystal, periodic lattice, optional FEM')
62for x,t
in [(.3,
'DDD'),(2.7,
'Elastic deformation'),(5.1,
'Inclusions'),(7.5,
'Cluster dynamics')]:
63 box(ax,x,1.8,2.2,.8,t);
arrow(ax,(x+1.1,3.4),(x+1.1,2.6))
64box(ax,.3,.25,9.4,.8,
'MicrostructureContainer: solve / reSolve → minimum dt → output → update')
65for x
in [.3,2.7,5.1,7.5]:
arrow(ax,(x+1.1,1.8),(x+1.1,1.05))
66save(fig,
'multiphysics')
68fig=plt.figure(figsize=(6,4));ax=fig.add_subplot(111,projection=
'3d');t=np.linspace(0,2*np.pi,120)
69ax.plot(np.cos(t),np.sin(t),0*t,color=blue);ax.plot(np.cos(t),0*t,np.sin(t),color=blue,alpha=.3)
70v=np.array([.55,.45,.7]);v/=np.linalg.norm(v)
71for p,l
in [(np.array([0,0,1.3]),
'e₃ = R / R'),(v,
'κ(θ, φ)')]:ax.quiver(0,0,0,*p,color=orange);ax.text(*p,l,fontsize=10)
72ax.set_axis_off();ax.set_box_aspect((1,1,1));ax.view_init(20,30);
save(fig,
'fourier_sphere')
74fig,ax=plt.subplots(figsize=(7,2.8));a=np.logspace(-4,4,400)
75ax.semilogx(a,1/(1+a),label=
'Applied stress weight',color=blue)
76ax.semilogx(a,a/(1+a),label=
'Imposed strain weight',color=orange)
77ax.set(xlabel=
'Scalar stiffness ratio α',ylabel=
'Weight');ax.legend(frameon=
False,fontsize=10);ax.grid(alpha=.15);fig.tight_layout();
save(fig,
'controller_weights')
78fig,ax=plt.subplots(figsize=(7,2.8));x=np.linspace(-3,3,500)
79ax.plot(x,np.sqrt(x*x+.3**2),color=orange,label=
'a = 0.3');ax.plot(x,np.abs(x),color=blue,ls=
'--',label=
'a = 0')
80ax.set(xlabel=
'Signed separation along a line',ylabel=
'Regularized distance Rₐ');ax.legend(frameon=
False);ax.grid(alpha=.15);fig.tight_layout();
save(fig,
'regularized_distance')
84runpy.run_path(str(ROOT /
"scripts/create_polycrystal_figure.py"))
87runpy.run_path(str(ROOT /
"scripts/create_entity_figures.py"))
89runpy.run_path(str(ROOT /
"scripts/create_periodicity_figure.py"))
91runpy.run_path(str(ROOT /
"scripts/create_mesh_library_figure.py"))