oILAB
Loading...
Searching...
No Matches
example_inverse_design.py
Go to the documentation of this file.
1import numpy as np
2from fractions import Fraction
3import pyoilab as gb
4
5from bilayers import (
6 make_ab_graphene_bilayer,
7 make_aa_prime_mos2_bilayer,
8 heterodeform_bilayer,
9)
10from inverse_design_core import run_inverse_design, print_fraction_matrix
11from lammps_writer import write_lammps_data, summarize_configuration
12
13
14# ------------------------------------------------------------
15# Step 1: Choose reference bilayer
16# ------------------------------------------------------------
17#bilayer = make_ab_graphene_bilayer(3.5)
18bilayer = make_aa_prime_mos2_bilayer(6.5)
19
20
21# ------------------------------------------------------------
22# Step 2: Specify inverse-design input
23# ------------------------------------------------------------
24# Example shown here: 1D simple network
25# Replace these using the combinations listed in README.md.
26b1u = np.array([Fraction(0, 1), Fraction(1, 1)], dtype=object)
27b2u = np.array([Fraction(0, 1), Fraction(0, 1)], dtype=object)
28b3u = np.array([Fraction(0, 1), Fraction(0, 1)], dtype=object)
29
30l1u = np.array([Fraction(-1, 1), Fraction(1, 1)], dtype=object)
31l2u = np.array([Fraction(121, 1), Fraction(119, 1)], dtype=object)
32
33beta = Fraction(1, 1)
34
35# ------------------------------------------------------------
36# Step 3: Compute deformation gradient F
37# ------------------------------------------------------------
38# By default we use the bottom-layer lattice as the reference lattice in the
39# inverse-design formulas.
40result = run_inverse_design(
41 A=bilayer.bottom_layer.A,
42 b1u=b1u,
43 b2u=b2u,
44 b3u=b3u,
45 l1u=l1u,
46 l2u=l2u,
47 beta=beta,
48)
49
50F = result.F
51
52print_fraction_matrix("Q", result.Q)
53
54print_fraction_matrix("Q^{-1}", result.Qinv)
55
56print("\nF = ")
57print(F)
58
59
60# ------------------------------------------------------------
61# Step 4: Heterodeform the bilayer
62# ------------------------------------------------------------
63# Default convention in this example:
64# bottom layer is deformed, top layer is fixed
65# The user may freely change this.
66hetero_bilayer = heterodeform_bilayer(
67 bilayer,
68 F_bottom=F,
69 F_top=np.eye(2),
70)
71
72sigmaA = abs(hetero_bilayer.bicrystal.sigmaA)
73sigmaB = abs(hetero_bilayer.bicrystal.sigmaB)
74exp_number_of_atoms_top_layer = sigmaA * len(hetero_bilayer.top_layer.basis_atoms)
75exp_number_of_atoms_bottom_layer = sigmaB * len(hetero_bilayer.bottom_layer.basis_atoms)
76print("\nExpected number of atoms in the top layer = ", exp_number_of_atoms_top_layer)
77print("Expected number of atoms in the bottom layer = ", exp_number_of_atoms_bottom_layer)
78print("Expected total number of atoms in the bilayer = ", exp_number_of_atoms_top_layer+exp_number_of_atoms_bottom_layer)
79
80# ------------------------------------------------------------
81# Step 5: Choose CSL vectors
82# ------------------------------------------------------------
83bicrystal = hetero_bilayer.bicrystal
84
85# Primitive CSL vectors: The user may instead choose any other integer combination of CSL basis vectors.
86ell1 = gb.LatticeVector2D(np.array([1, 0], dtype=np.int64), bicrystal.csl)
87ell2 = gb.LatticeVector2D(np.array([0, 1], dtype=np.int64), bicrystal.csl)
88
89
90
91# ------------------------------------------------------------
92# Step 6: Build atomic positions in the chosen CSL cell
93# ------------------------------------------------------------
94positions, atom_types, charges, labels, molecule_ids = hetero_bilayer.box(ell1, ell2)
95
96print(f"\nNumber of atoms in box = {len(positions)}")
97
98
99# ------------------------------------------------------------
100# Step 7: Optional rotation so ell1 aligns with the x-axis
101# ------------------------------------------------------------
102do_rotate = True
103
104ell1_cart = np.array(ell1.cartesian(), dtype=float)
105ell2_cart = np.array(ell2.cartesian(), dtype=float)
106
107if do_rotate:
108 theta = np.arctan2(ell1_cart[1], ell1_cart[0])
109
110 R = np.array([
111 [np.cos(-theta), -np.sin(-theta)],
112 [np.sin(-theta), np.cos(-theta)],
113 ])
114
115 positions = [
116 tuple(R @ np.array([x, y])) + (z,)
117 for (x, y, z) in positions
118 ]
119
120 ell1_cart = R @ ell1_cart
121 ell2_cart = R @ ell2_cart
122
123
124# ------------------------------------------------------------
125# Step 8: Write LAMMPS data file
126# ------------------------------------------------------------
127summarize_configuration(positions, atom_types, ell1_cart, ell2_cart)
128write_lammps_data(
129 filename="initial.data",
130 positions=positions,
131 atom_types=atom_types,
132 charges=charges,
133 molecule_ids=molecule_ids,
134 ell1=ell1_cart,
135 ell2=ell2_cart,
136 atom_style=bilayer.atom_style,
137 z_padding=40.0,
138)
139
140print("\nWrote LAMMPS data file: initial.data")