Bugfix virial with fix electrode
This commit is contained in:
committed by
Shern Tee
parent
4bb71195aa
commit
49c84dbe1e
@ -1,2 +1,3 @@
|
||||
*.csv
|
||||
*.txt
|
||||
*.lammpstrj
|
||||
|
||||
@ -17,14 +17,22 @@ q_ref = float(ref_line[3])
|
||||
inv11_ref = float(ref_line[4])
|
||||
inv12_ref = float(ref_line[5])
|
||||
b1_ref = float(ref_line[6])
|
||||
felec1_ref = float(ref_line[8])
|
||||
felyt1_ref = float(ref_line[10])
|
||||
press_ref = float(ref_line[12])
|
||||
|
||||
# out.csv
|
||||
with open(sys.argv[2]) as f:
|
||||
out_line = f.readlines()[-1].split(", ")
|
||||
e_out = float(out_line[0])
|
||||
q_out = float(out_line[1])
|
||||
press_out = float(out_line[2])
|
||||
|
||||
out_lines = [("energy", e_ref, e_out), ("charge", q_ref, q_out)]
|
||||
out_lines = [
|
||||
("energy", e_ref, e_out),
|
||||
("charge", q_ref, q_out),
|
||||
("pressure", press_ref, press_out),
|
||||
]
|
||||
|
||||
# vec.csv
|
||||
vec_file = "vec.csv"
|
||||
@ -44,6 +52,14 @@ if op.isfile(inv_file):
|
||||
inv12_out = float(inv_line[1])
|
||||
out_lines.append(("inv11", inv11_ref, inv11_out))
|
||||
|
||||
# forces.lammpstrj
|
||||
force_file = "forces.lammpstrj"
|
||||
with open(force_file) as f:
|
||||
lines = f.readlines()[9:]
|
||||
for name, i, f_ref in [("felec1", "1", felec1_ref), ("felyt1", "3", felyt1_ref)]:
|
||||
f_out = next(float(y[3]) for x in lines if (y := x.split()) and y[0] == i)
|
||||
out_lines.append((name, f_ref, f_out))
|
||||
|
||||
lines = []
|
||||
for label, ref, out in out_lines:
|
||||
error = rel_error(out, ref)
|
||||
|
||||
@ -1,12 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
from scipy.special import erf
|
||||
|
||||
SQRT2 = np.sqrt(2)
|
||||
SQRTPI_INV = 1 / np.sqrt(np.pi)
|
||||
COULOMB = 332.06371 # Coulomb constant in Lammps 'real' units
|
||||
QE2F = 23.060549
|
||||
NKTV2P = 68568.415 # pressure in 'real' units
|
||||
LENGTH = 10000 # convergence parameter
|
||||
LZ = 20
|
||||
|
||||
|
||||
def lattice(length):
|
||||
@ -26,6 +31,25 @@ def b_element(r, q, eta):
|
||||
return q * erf(eta * r) / r
|
||||
|
||||
|
||||
def force_gauss(r, qq, eta):
|
||||
etar = eta * r
|
||||
return (qq / np.square(r)) * (
|
||||
erf(etar) - 2 * etar * SQRTPI_INV * np.exp(-np.square(etar))
|
||||
)
|
||||
|
||||
|
||||
def force_point(r, qq):
|
||||
return qq / np.square(r)
|
||||
|
||||
|
||||
def force_component(dx, d, qq, eta=None):
|
||||
if eta:
|
||||
return np.sum(dx / d * force_gauss(d, qq, eta))
|
||||
else:
|
||||
return np.sum(dx / d * force_point(d, qq))
|
||||
|
||||
|
||||
time_start = time.perf_counter()
|
||||
a = 1 # nearest neighbor distance i.e. lattice constant / sqrt(2)
|
||||
x_elec = [-2, 2]
|
||||
x_elyt = [-1, 1]
|
||||
@ -36,8 +60,20 @@ v = np.array([-0.5, 0.5]) * (QE2F / COULOMB)
|
||||
# distances to images within electrode and to opposite electrode
|
||||
distances = a * np.linalg.norm(lattice(LENGTH), axis=1)
|
||||
opposite_distances = np.sqrt(np.square(distances) + distance_plates**2)
|
||||
image_distances = []
|
||||
for x in x_elec:
|
||||
image_distances.append([])
|
||||
for y in x_elyt:
|
||||
image_distances[-1].append(np.sqrt(np.square(distances) + np.abs(y - x) ** 2))
|
||||
image_elyt_distances = [[None for _ in range(len(x_elyt))] for _ in range(len(x_elyt))]
|
||||
for i, (xi, qi) in enumerate(zip(x_elyt, q_elyt)):
|
||||
for j, (xj, qj) in list(enumerate(zip(x_elyt, q_elyt)))[i + 1 :]:
|
||||
image_elyt_distances[i][j] = np.sqrt(
|
||||
np.square(distances) + np.abs(xj - xi) ** 2
|
||||
)
|
||||
|
||||
for name, eta_elec in [("", [2.0, 2.0]), ("_eta_mix", [0.5, 3.0])]:
|
||||
# for name, eta_elec in [("", [2.0, 2.0])]:
|
||||
eta_mix = np.prod(eta_elec) / np.sqrt(np.sum(np.square(eta_elec)))
|
||||
# self interaction and within original box
|
||||
A_11 = np.sqrt(2 / np.pi) * eta_elec[0]
|
||||
@ -55,22 +91,18 @@ for name, eta_elec in [("", [2.0, 2.0]), ("_eta_mix", [0.5, 3.0])]:
|
||||
|
||||
# electrode-electrolyte interaction
|
||||
b = []
|
||||
for x, eta in zip(x_elec, eta_elec):
|
||||
for i, (x, eta) in enumerate(zip(x_elec, eta_elec)):
|
||||
bi = 0
|
||||
for y, q in zip(x_elyt, q_elyt):
|
||||
d = abs(y - x)
|
||||
bi += b_element(d, q, eta)
|
||||
image_distances = np.sqrt(np.square(distances) + d**2)
|
||||
bi += 4 * np.sum(b_element(image_distances, q, eta))
|
||||
for j, (y, q) in enumerate(zip(x_elyt, q_elyt)):
|
||||
bi += b_element(np.abs(y - x), q, eta)
|
||||
bi += 4 * np.sum(b_element(image_distances[i][j], q, eta))
|
||||
b.append(bi)
|
||||
b = np.array(b)
|
||||
|
||||
# electrolyte-electrolyte energy
|
||||
elyt_11 = 4 * np.sum(1 / distances)
|
||||
distance_elyt = x_elyt[1] - x_elyt[0]
|
||||
elyt_12 = 1 / distance_elyt + 4 * np.sum(
|
||||
1 / np.sqrt(np.square(distances) + distance_elyt**2)
|
||||
)
|
||||
elyt_12 = 1 / distance_elyt + 4 * np.sum(1 / image_elyt_distances[0][1])
|
||||
elyt = np.array([[elyt_11, elyt_12], [elyt_12, elyt_11]])
|
||||
energy_elyt = 0.5 * np.dot(q_elyt, np.dot(elyt, q_elyt))
|
||||
|
||||
@ -78,9 +110,48 @@ for name, eta_elec in [("", [2.0, 2.0]), ("_eta_mix", [0.5, 3.0])]:
|
||||
q = np.dot(inv, v - b)
|
||||
energy = COULOMB * (0.5 * np.dot(q, np.dot(A, q)) + np.dot(b, q) + energy_elyt)
|
||||
|
||||
# forces in out-of-plane direction
|
||||
f_elec = np.zeros(len(x_elec))
|
||||
f_elyt = np.zeros(len(x_elyt))
|
||||
# electrode-electrode
|
||||
dx = x_elec[1] - x_elec[0]
|
||||
fij_box = force_component(dx, np.abs(dx), q[0] * q[1], eta_mix)
|
||||
fij_img = 4 * force_component(dx, opposite_distances, q[0] * q[1], eta_mix)
|
||||
f_elec[0] -= fij_box + fij_img
|
||||
f_elec[1] += fij_box + fij_img
|
||||
# electrode-electrolyte
|
||||
for i, (xi, qi, etai) in enumerate(zip(x_elec, q, eta_elec)):
|
||||
for j, (xj, qj) in enumerate(zip(x_elyt, q_elyt)):
|
||||
dx = xj - xi
|
||||
fij_box = force_component(dx, np.abs(dx), qi * qj, etai)
|
||||
fij_img = 4 * force_component(dx, image_distances[i][j], qi * qj, etai)
|
||||
f_elec[i] -= fij_box + fij_img
|
||||
f_elyt[j] += fij_box + fij_img
|
||||
# electrolyte-electrolyte
|
||||
for i, (xi, qi) in enumerate(zip(x_elyt, q_elyt)):
|
||||
for j, (xj, qj) in list(enumerate(zip(x_elyt, q_elyt)))[i + 1 :]:
|
||||
dx = xj - xi
|
||||
fij_box = force_component(dx, np.abs(dx), qi * qj)
|
||||
fij_img = 4 * force_component(dx, image_elyt_distances[i][j], qi * qj)
|
||||
f_elyt[i] -= fij_img + fij_box
|
||||
f_elyt[j] += fij_img + fij_box
|
||||
# force units
|
||||
assert np.abs(np.sum(f_elec) + np.sum(f_elyt)) < 1e-8
|
||||
f_elec *= COULOMB
|
||||
f_elyt *= COULOMB
|
||||
|
||||
# Virial
|
||||
volume = a**2 * LZ
|
||||
virial = 0.0
|
||||
for x, f in [(x_elec, f_elec), (x_elyt, f_elyt)]:
|
||||
virial += np.dot(x, f)
|
||||
pressure = NKTV2P * virial / volume
|
||||
|
||||
with open(f"plate_cap{name}.csv", "w") as f:
|
||||
f.write(
|
||||
"length, energy / kcal/mol, q1 / e, q2 / e, inv11 / A, inv12 / A, b1 / e/A, b2 / e/A\n"
|
||||
"length, energy / kcal/mol, q1 / e, q2 / e, inv11 / A, inv12 / A"
|
||||
+ ", b1 / e/A, b2 / e/A, felec1 / kcal/mol/A, felec2 / kcal/mol/A"
|
||||
+ ", felyt1 / kcal/mol/A, felyt2 / kcal/mol/A, press\n"
|
||||
)
|
||||
f.write(
|
||||
", ".join(
|
||||
@ -93,7 +164,14 @@ for name, eta_elec in [("", [2.0, 2.0]), ("_eta_mix", [0.5, 3.0])]:
|
||||
f"{inv[0, 1]:.10f}",
|
||||
f"{b[0]:.8f}",
|
||||
f"{b[1]:.8f}",
|
||||
f"{f_elec[0]:.5f}",
|
||||
f"{f_elec[1]:.5f}",
|
||||
f"{f_elyt[0]:.5f}",
|
||||
f"{f_elyt[1]:.5f}",
|
||||
f"{pressure:.2f}",
|
||||
]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
time_end = time.perf_counter()
|
||||
print(f"{time_end - time_start:0.4f} seconds")
|
||||
|
||||
@ -19,4 +19,8 @@ compute qtop top reduce sum v_q
|
||||
compute compute_pe all pe
|
||||
variable vpe equal c_compute_pe
|
||||
variable charge equal c_qtop
|
||||
fix fxprint all print 1 "${vpe}, ${charge}" file "out.csv"
|
||||
compute press all pressure NULL virial
|
||||
variable p3 equal c_press[3]
|
||||
fix fxprint all print 1 "${vpe}, ${charge}, ${p3}" file "out.csv"
|
||||
|
||||
dump dump_forces all custom 1 forces.lammpstrj id fx fy fz
|
||||
|
||||
Reference in New Issue
Block a user