Modified scripts, small fixes and changed ordered caalculation

This commit is contained in:
Kenneth Jao 2022-02-25 16:22:36 -05:00
parent b32e0b1a8c
commit de65751b48
20 changed files with 1471 additions and 190 deletions

230
scripts/continuation.py Normal file
View File

@ -0,0 +1,230 @@
import argparse, numpy as np, os
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
from multiprocessing import Pool, cpu_count
from pathlib import Path
from squish import Simulation, DomainParams, Energy, ordered, Diagram
from squish.common import OUTPUT_DIR
from script_tools import (
RC_SETTINGS,
get_args,
get_data,
get_simulation_data,
get_ordered_data,
)
NAME = "Continuation"
def proc(combo):
dia, min_order, min_disorder, hex_ratios, alphas, sim_alphas, sim_energies, offset, length, num_frames = (
combo
)
sim = dia.sim
out_folder = sim.path / "continuation"
plt.rcParams.update(RC_SETTINGS)
plt.rcParams.update({"figure.constrained_layout.use": False})
fig = plt.figure(figsize=(30, 15))
e_hex = ordered.e_hex(sim.domains[0])
for i in range(length):
gs = fig.add_gridspec(1, 2)
gs.update(left=0, right=0.98, top=0.93, bottom=0.12, wspace=0.08)
ax = fig.add_subplot(gs[1])
curr_alpha = sim.domains[i].w / sim.domains[i].h
curr_vee = 100 * (sim.energies[i] / sim.domains[i].n - e_hex)
ax.plot(
alphas,
100 * min_order,
color="C1",
label="Min Ordered",
zorder=10,
linestyle="dotted",
)
ax.plot(
alphas,
100 * min_disorder,
color="C0",
label="Min Disordered",
zorder=11,
linestyle="dotted",
)
ax.plot(
sim_alphas[:175],
100 * sim_energies[:175],
color="C2",
label="_nolegend_",
linestyle="dashed",
)
ax.plot(
sim_alphas[174:], 100 * sim_energies[174:], color="C2", label="Continuation"
)
ax.scatter(
hex_ratios, [0] * len(hex_ratios), color="C2", s=120, marker="H", zorder=50
)
ax.scatter(
curr_alpha,
curr_vee,
s=250,
facecolors="none",
edgecolors="C6",
linewidth=4,
zorder=100,
)
ax.set_xlim(0.3, 1)
ax.set_xticks([round(w, 2) for w in alphas[::10]])
ax.set_xticklabels([f"{round(w, 3):.2f}" for w in alphas[::10]], rotation=90)
# start, end = ax.get_ylim()
# space = np.linspace(0, end, 20)
space = np.arange(0, 10, 0.5)
ax.set_ylim(-0.15, 9.5)
ax.set_yticks(space[::-1])
ax.ticklabel_format(axis="y", style="sci")
ax.set_xlabel("Aspect Ratio")
ax.set_ylabel(r"VEE $\left[\times 10^{2}\right]$")
ax.legend(loc="upper center")
# ax.legend()
ax.grid(zorder=0)
props = dict(boxstyle="round", facecolor="white", alpha=0.8, zorder=20)
ax.text(
0.873,
0.97,
f"N={sim.domains[i].n}",
transform=ax.transAxes,
verticalalignment="top",
bbox=props,
)
ax1 = fig.add_subplot(gs[0])
dia.voronoi_plot(i, ax1)
fig.savefig(out_folder / f"img{i+offset:05}.png")
fig.clear()
j = len(list((sim.path / "continuation").iterdir()))
hashes = int(21 * j / num_frames)
print(
f'Generating frames... |{"#"*hashes}{" "*(20-hashes)}|'
+ f" {j}/{num_frames} frames rendered.",
flush=True,
end="\r",
)
print(flush=True)
def main():
parser = argparse.ArgumentParser(
description="Makes video of analytic continuation."
)
parser.add_argument(
"sims_path",
metavar="sim_dir",
help="folder that contains simulation data at various aspect ratios for some N",
)
parser.add_argument(
"shrink_sim",
metavar="continuation_sim",
help="simulation that contains the continuation data",
)
args = parser.parse_args()
sims_path = Path(args.sims_path)
data, n, r = get_data(
sims_path / "package.pkl", get_simulation_data, args=(sims_path,)
)
domain, alphas = DomainParams(n, 1, 1, r), data["all"]["alpha"]
ordered_data = get_data(
OUTPUT_DIR / "OrderedCache" / f"{n}.pkl",
get_ordered_data,
args=(domain, alphas),
)
min_disorder, max_disorder = [], []
for i, energies in enumerate(data["distinct"]["Energy"]):
disorder_energies = []
for j, energy in enumerate(energies):
if not data["distinct"]["Ordered"][i][j]:
disorder_energies.append(energy)
min_disorder.append(min(disorder_energies))
max_disorder.append(max(disorder_energies))
min_order, min_order_coer = [], []
for i, energies in enumerate(ordered_data["Energy"]):
min_order.append(energies[0])
min_order_coer.append(ordered_data["Coercivity"][i][0])
e_hex = ordered.e_hex(domain)
min_disorder = np.array(min_disorder) / domain.n - e_hex
max_disorder = np.array(max_disorder) / domain.n - e_hex
min_order = np.array(min_order) / domain.n - e_hex
hex_ratios = ordered.hexagon_alpha(domain.n)
sim = Simulation.from_file(Path(args.shrink_sim))
sim_alphas = np.array([x.w / x.h for x in sim.frames])
sim_energies = np.array([x.energy for x in sim.frames]) / sim.domain.n - e_hex
dia = Diagram(sim, ["voronoi"])
out_folder = sim.path / "continuation"
out_folder.mkdir(exist_ok=True)
combo_list = []
for i in range(cpu_count()):
start, end = (
int(i * len(sim) / cpu_count()),
int((i + 1) * len(sim) / cpu_count()),
)
new_dia = Diagram(None, ["voronoi"])
new_dia.sim = dia.sim.slice(list(range(start, end)))
combo_list.append(
(
new_dia,
min_order,
min_disorder,
hex_ratios,
alphas,
sim_alphas,
sim_energies,
start,
len(sim.frames[start:end]),
len(sim),
)
)
print("Starting figure generation...", flush=True)
with Pool(cpu_count()) as pool:
for _ in pool.imap_unordered(proc, combo_list):
pass
video_path = sim.path / (NAME + ".mp4")
fps = 30
print("Assembling MP4...", flush=True)
os.system(
f"ffmpeg -hide_banner -loglevel error -r {fps} -i"
+ f' "{sim.path}/continuation/img%05d.png"'
+ f" -c:v libx264 -crf 18 -preset slow -pix_fmt yuv420p -vf"
+ f' "scale=trunc(iw/2)*2:trunc(ih/2)*2" -f mp4 "{video_path}"'
)
print(f"Wrote to {video_path}.")
if __name__ == "__main__":
os.environ["QT_LOGGING_RULES"] = "*=false"
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")

View File

@ -0,0 +1,78 @@
import numpy as np, os, csv
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
from squish import Simulation, DomainParams, ordered
from squish.common import OUTPUT_DIR
from script_tools import (
RC_SETTINGS,
get_args,
get_data,
get_simulation_data,
get_ordered_data,
)
NAME = "Cumulative-CellVEE"
ALPHA = 1.0
def main():
sims_path, regen = get_args(
"Anti-cumulative distribution of VEE and percent of equilibria for fixed alpha",
"folders that contains various N simulations to plot",
)
sim, frames = Simulation.load(sims_path)
vees = np.linspace(-3, 3, 10000)
e_hex = ordered.e_hex(sim.domain)
energies = {"all": np.empty(0, dtype=float)}
counts = {}
for frame in frames:
energy = frame["stats"]["site_energies"] - e_hex
defects = np.count_nonzero(frame["stats"]["site_edge_count"] != 6)
if defects not in energies:
energies[defects] = np.empty(0, dtype=float)
energies[defects] = np.append(energies[defects], energy)
energies["all"] = np.append(energies["all"], energy)
all_count = None
for defect, energy in energies.items():
count = np.empty(vees.shape, dtype=float)
for i, vee in enumerate(vees):
count[i] = np.count_nonzero(energy >= vee)
count = 100 * count / len(energy)
if defect == "all":
all_count = count
else:
counts[defect] = count
plt.rcParams.update(RC_SETTINGS)
fig = plt.figure(figsize=(15, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
for defect, count in sorted(counts.items()):
if defect == min(counts.keys()):
ax.plot(vees, count, label=fr"$\mathrm{{D}}={defect}$", color="C0")
elif defect == max(counts.keys()):
ax.plot(vees, count, label=fr"$\mathrm{{D}}={defect}$", color="C1")
else:
ax.plot(vees, count, linestyle="dotted", alpha=0.3)
ax.plot(vees, all_count, label="All", color="black")
ax.set_xlim(-2.5, 2)
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
ax.set_xlabel(r"VEE")
ax.set_ylabel("Percent of Voronoi Regions")
ax.grid(zorder=0)
ax.legend()
fig.savefig(OUTPUT_DIR / (NAME + ".png"))
print(f"Wrote to {OUTPUT_DIR / (NAME + '.png')}")
if __name__ == "__main__":
main()

View File

@ -1,4 +1,4 @@
import numpy as np, os
import numpy as np, os, csv
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
@ -48,6 +48,10 @@ def main():
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
my_cool_data = [["VEE", 61, 67, 73, 81, 84, 100]]
for vee in np.linspace(0, 0.06, 10000):
my_cool_data.append([vee])
for j, package in enumerate(packages):
data, ordered_data, domain = package
e_hex = ordered.e_hex(domain)
@ -66,6 +70,9 @@ def main():
counts[i] = np.count_nonzero(energies >= vee)
counts = 100 * counts / len(energies)
for i, count in enumerate(counts):
my_cool_data[i + 1].append(count)
ax.plot(
100 * vees[: index + 1],
counts[: index + 1],
@ -80,6 +87,10 @@ def main():
color=f"C{j}",
)
with open("cumulative-vee.csv", "w") as csvfile:
writer = csv.writer(csvfile)
writer.writerows(my_cool_data)
ax.set_xlim(0, 6.3)
ax.yaxis.set_major_formatter(mtick.PercentFormatter())

213
scripts/cumulative_vee_2.py Normal file
View File

@ -0,0 +1,213 @@
import numpy as np, os, csv
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
from scipy.optimize import curve_fit
from squish import Simulation, DomainParams, ordered
from squish.common import OUTPUT_DIR
from script_tools import (
RC_SETTINGS,
get_args,
get_data,
get_simulation_data,
get_ordered_data,
)
NAME = "Cumulative-VEE-AcrossN"
NAME2 = "Cumulative-VEE-RelError"
NAME3 = "Cumulative-VEE-Alphas"
def main():
sims_path, regen = get_args(
"Anti-cumulative distribution of VEE and percent of equilibria for fixed alpha",
"folders that contains various N simulations to plot",
)
vees = np.linspace(0, 0.06, 10000)
def f(path):
files = list(path.iterdir())
files = [f for f in files if f.is_dir()]
data = {}
for i, fol in enumerate(files):
sim, frames = Simulation.load(fol)
e_hex = ordered.e_hex(sim.domain)
energies = []
for frame in frames:
energies.append(frame["energy"] / sim.domain.n - e_hex)
energies = np.array(energies)
counts = np.empty(vees.shape, dtype=float)
for j, vee in enumerate(vees):
counts[j] = np.count_nonzero(energies >= vee)
counts = 100 * counts / len(energies)
data[sim.domain.n] = counts
hashes = int(20 * i / len(files))
print(
f'Loading simulations... |{"#"*hashes}{" "*(20-hashes)}|'
+ f" {i+1}/{len(files)} simulations loaded.",
flush=True,
end="\r",
)
print(flush=True)
return data
all_data = {}
alpha_files = list(sims_path.iterdir())
alpha_files = [f for f in alpha_files if f.is_dir()]
for fol in alpha_files:
all_data[float(fol.name)] = get_data(
fol / "Cumulative2.pkl", f, args=(fol,), regen=regen
)
data = all_data[1.0]
points = []
avgs = {}
# print((data[106] + data[104]) / 2 - data[105])
for n in range(105, 396):
arr = np.zeros(data[100].shape)
for i in range(1, 6):
arr += data[n - i] + data[n + i]
arr += data[n]
arr /= 11
avgs[n] = arr
points.append(np.linalg.norm(data[n] - arr) / np.linalg.norm(arr))
plt.rcParams.update(RC_SETTINGS)
fig = plt.figure(figsize=(15, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
ax.plot(100 * vees, avgs[395], color="black", label=r"Average", zorder=50)
for n, counts in sorted(data.items()):
if n in range(390, 401):
if n == 392:
ax.plot(100 * vees, counts, label="N=392", color="C0")
elif n == 397:
ax.plot(100 * vees, counts, label="N=397", color="C1")
elif n == 395:
ax.plot(100 * vees, counts, color="black", linestyle="dashed")
else:
ax.plot(
100 * vees,
counts,
label="_nolegend_",
linestyle="dashed",
alpha=0.5,
)
ax.set_xlim(0, 6.3)
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
ax.set_xlabel(r"VEE $\left[\times 10^{2}\right]$")
ax.set_ylabel("Percent of Equilibria")
ax.grid(zorder=0)
ax.legend()
fig.savefig(OUTPUT_DIR / (NAME + ".png"))
print(f"Wrote to {OUTPUT_DIR / (NAME + '.png')}")
fig = plt.figure(figsize=(30, 8))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
ns = np.array(range(105, 396))
ax.scatter(ns, points)
log_slope, _ = np.polyfit(np.log10(ns), np.log10(points), 1)
print(log_slope)
def g(x, k):
return k * (x ** log_slope)
# return a * np.e ** (-b * (x - c))
params, covs = curve_fit(g, ns, points, p0=[5000])
ax.plot(np.arange(50, 500), g(np.arange(50, 500), *params), color="C1")
# with open("cumul.csv", "w") as csvfile:
# csvwriter = csv.writer(csvfile)
# csvwriter.writerows(zip(ns, points))
print(params)
ax.set_xlim(95, 405)
ax.set_ylim(0, 0.24)
ax.set_xlabel("N")
ax.grid(zorder=0)
# ax.legend()
fig.savefig(OUTPUT_DIR / (NAME2 + ".png"))
print(f"Wrote to {OUTPUT_DIR / (NAME2 + '.png')}")
fig = plt.figure(figsize=(15, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
alpha_avgs = {}
for alpha, data in all_data.items():
if len(data.values()) == 0 or alpha == 1.0:
continue
alpha_avgs[alpha] = sum(data.values()) / 11
for alpha, data in all_data.items():
if alpha == 1.0:
continue
for n, counts in data.items():
# ax.plot(100 * vees, counts, linestyle="dotted", alpha=0.4)
continue
pad = 100
alpha_prob_dens = {}
for alpha, avg in alpha_avgs.items():
if alpha == 1.0:
continue
mov_avgs = np.array(
[np.mean(avg[max(0, i - pad) : i + pad]) for i in range(len(avg))]
)
d_avgs = np.gradient(mov_avgs, vees[1] - vees[0])
alpha_prob_dens[alpha] = d_avgs / (np.sum(d_avgs) * (vees[1] - vees[0]))
a1_prob_dens = {}
for n, avg in avgs.items():
mov_avgs = np.array(
[np.mean(avg[max(0, i - pad) : i + pad]) for i in range(len(avg))]
)
d_avgs = np.gradient(mov_avgs, vees[1] - vees[0])
a1_prob_dens[n] = d_avgs / (np.sum(d_avgs) * (vees[1] - vees[0]))
for alpha, prob_dens in sorted(alpha_prob_dens.items()):
if alpha in [0.3, 0.5, 0.7, 0.9, 1.0]:
continue
ax.plot(100 * vees, prob_dens, label=fr"$\alpha={alpha:.1f}$")
for n, prob_dens in sorted(a1_prob_dens.items()):
if n in [255, 315, 335, 355, 375]:
ax.plot(100 * vees, prob_dens, label=f"N={n}")
if n == 395:
ax.plot(100 * vees, prob_dens, label=f"N={n}", color="black")
# ax.plot(100 * vees, prob_dens, label=r"$\alpha = 1.0$", zorder=50, color="black")
# ax.plot(100 * vees, avgs[395], zorder=50, color="black")
ax.set_xlim(0, 6.3)
# ax.yaxis.set_major_formatter(mtick.PercentFormatter())
ax.set_xlabel(r"VEE $\left[\times 10^{2}\right]$")
# ax.set_ylabel("Percent of Equilibria")
ax.grid(zorder=0)
ax.legend()
fig.savefig(OUTPUT_DIR / (NAME3 + ".png"))
print(f"Wrote to {OUTPUT_DIR / (NAME3 + '.png')}")
if __name__ == "__main__":
main()

View File

@ -27,14 +27,22 @@ def main():
defects = np.array(defects)
energy = 100 * np.array(energy)
counts = {}
for i in range(70):
counts[i] = np.count_nonzero(defects == i)
print(counts)
for k, v in counts.items():
if k % 2 == 1 and v > 0:
print(k, v)
plt.rcParams.update(RC_SETTINGS)
fig = plt.figure(figsize=(18, 15))
fig = plt.figure(figsize=(15, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
m, b = np.polyfit(defects, energy, 1)
ax.scatter(defects, energy, color="C0", marker="*", s=100)
ax.plot(np.arange(0, 64), np.arange(0, 64) * m + b, zorder=3, color="C1")
ax.scatter(defects, energy, color="C0", marker="*", s=70, zorder=2)
ax.plot(np.arange(0, 65), np.arange(0, 65) * m + b, zorder=3, color="C1")
ax.scatter(0, b, color="C2", s=500, marker="*", zorder=50)
@ -49,10 +57,10 @@ def main():
ax.set_xlim(0, 64)
ax.set_xticks(np.arange(0, 65, 8))
ax.set_ylim(0, 3.6)
ax.set_ylim(0, 3.7)
ax.set_yticks(np.arange(0, 3.6, 0.5))
ax.set_xlabel("Number of Defects")
ax.set_xlabel("$\mathrm{D}$")
ax.set_ylabel(r"VEE $\left[\times 10^{2}\right]$")
ax.grid(zorder=0)

View File

@ -0,0 +1,185 @@
import numpy as np, os, math
import matplotlib.pyplot as plt
from multiprocessing import Pool, cpu_count
from squish import Simulation, ordered, DomainParams
from squish.common import OUTPUT_DIR
from script_tools import RC_SETTINGS, get_args, get_data, get_ordered_data, format_data
NAME = "DefectEnergy"
NAME2 = "Frustration"
def proc(path):
sim, frames = Simulation.load(path)
domain = sim.domain
e_hex = ordered.e_hex(domain)
defects, energy = [], []
for frame in frames:
# if np.var(frame["stats"]["avg_radius"]) > 1e-8:
defects.append(np.count_nonzero(frame["stats"]["site_edge_count"] != 6))
energy.append(frame["energy"] / domain.n - e_hex)
configs = ordered.configurations(domain)
avg_defects = sum(defects) / len(defects)
avg_vee = sum(energy) / len(energy)
m, b = np.polyfit(defects, energy, 1)
alpha = domain.w / domain.h
return [alpha, tuple(domain), avg_defects, avg_vee, m, b]
def main():
sims_path, regen = get_args(
"Generates graph for Frustration at fixed N across alpha",
"folder that contains simulations at various alpha for fixed N",
)
def f():
data = {}
files = list(sims_path.iterdir())
files = [x for x in files if x.is_dir()]
with Pool(cpu_count()) as pool:
for i, res in enumerate(pool.imap_unordered(proc, files)):
data[res[0]] = res[1:]
hashes = int(21 * i / len(files))
print(
f'Processed alpha={res[0]:.3f} |{"#"*hashes}{" "*(20-hashes)}|'
+ f" {i+1}/{len(files)} simulations processed.",
flush=True,
end="\r",
)
print(flush=True)
return format_data(
data,
key_name="alpha",
col_names=[
"Domain",
"Average Defects",
"Average VEE",
"Energy Per Defect",
"Ground VEE",
],
)
plt.rcParams.update(RC_SETTINGS)
data = get_data(sims_path / (NAME + ".pkl"), f, regen=regen)
domain = DomainParams(*data["Domain"][0])
# ordered_data = get_data(
# OUTPUT_DIR / "OrderedCache" / f"{domain.n}.pkl",
# get_ordered_data,
# args=(domain, data["alpha"]),
# regen=regen,
# )
# min_order = []
# for i, energies in enumerate(ordered_data["Energy"]):
# min_order.append(energies[0])
alphas, avg_defects, avg_vees, epds, vee0s = (
data["alpha"],
data["Average Defects"],
100 * data["Average VEE"],
100 * data["Energy Per Defect"],
100 * data["Ground VEE"],
# 100 * (np.array(min_order) / domain.n - ordered.e_hex(domain)),
)
fig = plt.figure(figsize=(15, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
m0, b0 = np.polyfit(alphas, avg_defects, 1)
# m1, b1 = np.polyfit(ns, avg_defects * epds, 1)
lns1 = ax.plot(
alphas,
avg_defects,
color="C0",
alpha=0.5,
label=r"$\langle \mathrm{D} \rangle$",
)
ax.plot(alphas, m0 * alphas + b0, color="C0", linestyle="dashed")
lns2 = ax.plot(
alphas, 100 * epds, color="C1", alpha=0.5, label=r"$\zeta_1 \times 10^{4}$"
)
lns3 = ax.plot(
alphas,
10 * avg_defects * epds,
color="C2",
alpha=0.5,
label=r"$\mathcal{F} \times 10^{3}$",
)
# ax3.plot(ns, 100*(m1 * ns + b1) / 10, color="C2", linestyle="dashed")
ax.set_xlim(0.3, 1.0)
# ax.set_ylim(3, 37)
# ax.set_yticks(np.arange(5, 40, 5))
# ax2.set_ylim(-3 * 0.4, 18 + 3 * 0.4)
# ax2.set_yticks(np.arange(0, 20, 3))
# ax3.set_ylim(-3 * 0.5, 18 + 3 * 0.4)
# ax3.set_yticks([])
# ax3.spines["right"].set_visible(False)
# ax3.spines.right.set_position(("axes", 1.11))
# lns = lns1 + lns2 + lns3
# labs = [l.get_label() for l in lns]
ax.grid(zorder=0)
# ax.legend(lns, labs, loc="lower right")
ax.legend()
ax.set_xlabel("N")
# ax.set_ylabel(r"$\langle \mathrm{D} \rangle$")
# ax2.set_ylabel("VEE")
# ax2.set_ylabel(r"$\zeta \left[\times 10^{4} \right]$", color="C1")
# ax3.set_ylabel(r"Defect Energy $\left[\times 10^{3} \right]$", color="C2")
out = OUTPUT_DIR / (NAME + f" - N{domain.n}.png")
fig.savefig(out)
print(f"Wrote to {out}")
# return
fig = plt.figure(figsize=(20, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
ax.plot(alphas, vee0s, label="$\zeta_0$", color="C5")
ax.plot(alphas, avg_defects * epds, label=r"$\mathcal{F}$", color="C2")
ax.plot(alphas, avg_vees, label=r"$\langle \mathrm{VEE} \rangle$", color="C4")
# ax.plot(alphas, min_order, label="Minimum Ordered", color="C7", alpha=0.5)
print(np.linalg.norm(avg_vees - (vee0s + avg_defects * epds)))
ax.grid(zorder=0)
ax.legend()
ax.set_xlim(0.3, 1)
# ax.set_xticks([round(w, 2) for w in alphas[::10]])
# ax.set_xticklabels([f"{round(w, 3):.2f}" for w in alphas[::10]], rotation=90)
# ax.set_ylim(-0.1, 9.2)
# ax.set_yticks(np.arange(0, 9.6, 1))
ax.set_xlabel("Aspect Ratio")
ax.set_ylabel(r"VEE $\left[\times 10^{2}\right]$")
out = OUTPUT_DIR / (NAME2 + f" - N{domain.n}.png")
fig.savefig(out)
print(f"Wrote to {out}")
if __name__ == "__main__":
os.environ["QT_log10GING_RULES"] = "*=false"
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")

View File

@ -88,7 +88,7 @@ def main():
100 * data["Minimum Ordered VEE"],
)
fig = plt.figure(figsize=(18, 15))
fig = plt.figure(figsize=(15, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
# ax2 = ax.twinx()
@ -103,17 +103,18 @@ def main():
ax.plot(ns, m0 * ns + b0, color="C0", linestyle="dashed")
lns2 = ax.plot(
ns, 100 * epds, color="C1", alpha=0.5, label=r"$\zeta \times 10^{4}$"
ns, 100 * epds, color="C1", alpha=0.5, label=r"$\zeta_1 \times 10^{4}$"
)
lns3 = ax.plot(
ns,
100 * avg_defects * epds / 10,
10 * avg_defects * epds,
color="C2",
alpha=0.5,
label=r"$\mathcal{F} \times 10^{3}$",
)
# ax3.plot(ns, 100*(m1 * ns + b1) / 10, color="C2", linestyle="dashed")
ax.set_xlim(93, 407)
ax.set_ylim(3, 37)
ax.set_yticks(np.arange(5, 40, 5))
@ -140,23 +141,25 @@ def main():
fig.savefig(OUTPUT_DIR / (NAME + ".png"))
print(f"Wrote to {OUTPUT_DIR / (NAME + '.png')}")
return
fig = plt.figure(figsize=(30, 15))
# return
fig = plt.figure(figsize=(20, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
ax.plot(ns, vee0s, label="$\mathrm{VEE}_0$")
ax.plot(ns, avg_defects * epds, label=r"$\mathcal{F}$")
ax.plot(ns, avg_vees, label=r"$\langle \mathrm{VEE} \rangle$")
ax.plot(ns, min_order, label="Minimum Ordered", color="C5", alpha=0.5)
ax.plot(ns, vee0s, label="$\zeta_0$", color="C5")
ax.plot(ns, avg_defects * epds, label=r"$\mathcal{F}$", color="C2")
ax.plot(ns, avg_vees, label=r"$\langle \mathrm{VEE} \rangle$", color="C4")
ax.scatter(ns, min_order, label="Minimum Ordered", color="C7", alpha=0.8)
print(np.linalg.norm(avg_vees - (vee0s + avg_defects * epds)))
ax.grid(zorder=0)
ax.legend()
ax.set_ylim(0, 3)
ax.set_yticks(np.arange(0, 3.1, 0.5))
ax.set_xlim(95, 405)
ax.set_ylim(-0.1, 9.2)
ax.set_yticks(np.arange(0, 9.6, 1))
ax.set_xlabel("N")
ax.set_ylabel(r"VEE $\left[\times 10^{2}\right]$")

91
scripts/min_order_vee.py Normal file
View File

@ -0,0 +1,91 @@
import numpy as np, os, math
import matplotlib.pyplot as plt
from multiprocessing import Pool, cpu_count
from squish import Simulation, ordered, DomainParams
from squish.common import OUTPUT_DIR
from script_tools import RC_SETTINGS, get_args, get_data, format_data
NAME = "MinOrderVEE"
def get_min_vee(n):
domain = DomainParams(n, math.sqrt(n), math.sqrt(n), 4)
e_hex = ordered.e_hex(domain)
configs = ordered.configurations(domain)
min_vee, min_config = 1e10, None
for config in configs:
try:
rbar = ordered.avg_radius(domain, config)
except:
print(tuple(domain), config)
vee = (
2 * domain.w * domain.h
+ 2 * math.pi * domain.n * (domain.r ** 2 - 2 * domain.r * rbar)
) / domain.n - e_hex
if vee < min_vee and vee >= 0:
min_vee = vee
min_config = config
return (domain.n, min_vee, min_config)
def main():
def f():
data = {}
ns = list(range(1000, 5001))
with Pool(cpu_count()) as pool:
for i, res in enumerate(pool.imap_unordered(get_min_vee, ns)):
data[res[0]] = res[1:]
hashes = int(21 * i / len(ns))
print(
f'Processed N={res[0]} |{"#"*hashes}{" "*(20-hashes)}|'
+ f" {i+1}/{len(ns)} simulations processed.",
flush=True,
end="\r",
)
print(flush=True)
return format_data(
data, key_name="N", col_names=["Minimum Ordered VEE", "Config"]
)
plt.rcParams.update(RC_SETTINGS)
data = get_data(OUTPUT_DIR / "OrderedCache" / (NAME + ".pkl"), f)
ns, min_order, min_config = (
data["N"],
100 * data["Minimum Ordered VEE"],
data["Config"],
)
print(min_order, min_config)
fig = plt.figure(figsize=(20, 15))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
ax.scatter(ns, min_order)
ax.grid(zorder=0)
# ax.set_xlim(1000, 5000)
ax.set_xlabel("N")
ax.set_ylabel(r"VEE $\left[\times 10^{2}\right]$")
fig.savefig(OUTPUT_DIR / (NAME + ".png"))
print(f"Wrote to {OUTPUT_DIR / (NAME + '.png')}")
if __name__ == "__main__":
os.environ["QT_log10GING_RULES"] = "*=false"
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")

View File

@ -6,7 +6,7 @@ from squish import Simulation, ordered
from squish.common import OUTPUT_DIR
from script_tools import RC_SETTINGS, get_data, format_data
NAME = "OrdereredRBar"
NAME = "OrderedRBar"
def main():
@ -34,7 +34,7 @@ def main():
plt.rcParams.update(RC_SETTINGS)
fig = plt.figure(figsize=(20, 10))
fig = plt.figure(figsize=(18, 14.4))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
@ -53,6 +53,8 @@ def main():
color="black",
)
ax.set_ylim(0.51, 0.615)
ax.set_yticks(np.arange(0.52, 0.62, 0.03))
ax.set_ylabel(r"$\bar{r}$")
ax.grid(zorder=0)
ax.legend()

View File

@ -37,9 +37,14 @@ def main():
e_hex = ordered.e_hex(DomainParams(args.n, 1, 1, args.r))
for alpha, energies in zip(ordered_data["alpha"], ordered_data["Energy"]):
ax.scatter(
[alpha] * len(energies), 100 * (energies / args.n - e_hex), color="C0"
[alpha] * len(energies), 100 * (energies / args.n - e_hex), color="C0", s=25
)
hex_ratios = ordered.hexagon_alpha(args.n)
ax.scatter(
hex_ratios, [0] * len(hex_ratios), color="C2", s=200, marker="H", zorder=10
)
ax.set_xlim(0.3, 1)
ax.set_xticks(np.arange(0.3, 1.01, 0.1))

View File

@ -2,20 +2,13 @@ import argparse, numpy as np, os
from pathlib import Path
import matplotlib.pyplot as plt
from squish import DomainParams, Simulation
from squish import DomainParams, Simulation, ordered
from squish.common import OUTPUT_DIR
from script_tools import RC_SETTINGS, get_data, format_data
NAME = "Perturbations"
def toroidal_distance(domain: DomainParams, x: np.ndarray, y: np.ndarray) -> float:
dim = np.array([domain.w, domain.h])
absdist = np.abs(y - x)
wrap = dim * (absdist >= np.array([domain.w, domain.h]) / 2)
return np.linalg.norm(wrap - absdist)
def main():
parser = argparse.ArgumentParser(
description="Graphs perturbation graphs for a collection of simulations."
@ -60,8 +53,10 @@ def main():
)
data[delta]["norm"].append(
toroidal_distance(
end_sim.domain, adjusted, end_sim.frames[0].site_arr
np.linalg.norm(
ordered.toroidal_distance(
end_sim.domain, adjusted, end_sim.frames[0].site_arr
)
)
)
data[delta]["time"].append(sim.step_size * i)
@ -83,7 +78,7 @@ def main():
label=f"k = {data[delta]['k']}",
)
ax.set_title(r"Relaxation of Perturbations")
# ax.set_title(r"Relaxation of Perturbations")
ax.set_xlim([0, 60])
ax.set_yscale("log")

View File

@ -90,15 +90,15 @@ def main():
ax.set_xlabel("Aspect Ratio")
ax.set_ylabel(r"VEE $\left[\times 10^{2}\right]$", color="C2")
ax2.set_ylabel("Percent of Disordered Equilibria", color="C4")
ax2.set_ylabel("POD", color="C4")
# ax.legend(loc=(0.23, 0.5))
ax.grid(zorder=0)
props = dict(boxstyle="round", facecolor="white", alpha=0.8, zorder=20)
ax.text(
0.873,
0.97,
0.85,
0.92,
f"N={domain.n}",
transform=ax.transAxes,
verticalalignment="top",

85
scripts/rbar.py Normal file
View File

@ -0,0 +1,85 @@
import argparse, numpy as np, os
from multiprocessing import Pool, cpu_count
import matplotlib.pyplot as plt
from squish import Simulation, ordered
from squish.common import OUTPUT_DIR
from script_tools import RC_SETTINGS, get_args, get_data, format_data
NAME = "RBar"
def main():
sims_path, regen = get_args(
"Variance of average radius for as N gets larger",
"folders that contains various N simulations to plot",
)
def f():
files = list(sims_path.iterdir())
files = [f for f in files if f.is_dir()]
data = {}
for i, fol in enumerate(files):
sim, frames = Simulation.load(fol)
variances = []
for frame in frames:
rbars = np.sort(frame["stats"]["avg_radius"])
variances.append(np.var(rbars))
avg_variance = sum(variances) / len(variances)
data[sim.domain.n] = [avg_variance]
hashes = int(20 * i / len(files))
print(
f'Loading simulations... |{"#"*hashes}{" "*(20-hashes)}|'
+ f" {i+1}/{len(files)} simulations loaded.",
flush=True,
end="\r",
)
print(flush=True)
return format_data(data, key_name="N", col_names=["Average Variance"])
data = get_data(sims_path / (NAME + ".pkl"), f, regen=regen)
plt.rcParams.update(RC_SETTINGS)
fig = plt.figure(figsize=(18, 14.4))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
primes = [
i
for i in range(min(data["N"]), max(data["N"]))
if len(ordered.divisors(i)) == 2
]
prime_points = []
for i, n in enumerate(data["N"]):
if n in primes:
prime_points.append(data["Average Variance"][i])
ax.plot(data["N"], 1e5 * data["Average Variance"])
ax.scatter(primes, 1e5 * np.array(prime_points), marker="o", s=60)
ax.set_xlim(95, 405)
ax.set_ylim(0, 11)
# ax.set_ylim(0, args.max_n)
ax.set_xlabel("N")
ax.set_ylabel(r"$\sigma \left[\times 10^5 \right]$")
ax.grid(zorder=0)
# ax.legend()
fig.savefig(OUTPUT_DIR / (NAME + ".png"))
print(f"Wrote to {OUTPUT_DIR / (NAME + '.png')}")
if __name__ == "__main__":
os.environ["QT_log10GING_RULES"] = "*=false"
try:
main()
except KeyboardInterrupt:
print("Program terminated by user.")

View File

@ -26,7 +26,7 @@ RC_SETTINGS = {
"font.family": "cm",
"font.size": 40,
"text.usetex": True,
"text.latex.preamble": r"\usepackage{amsmath}",
"text.latex.preamble": r"\usepackage{amsmath, amsfonts}",
"figure.constrained_layout.use": True,
}

View File

@ -2,40 +2,40 @@ import numpy as np
import matplotlib.pyplot as plt
def torus_mesh(n, c, a):
def torus_mesh(n, r, R):
theta = np.linspace(0, 2 * np.pi, n)
phi = np.linspace(0, 2 * np.pi, n)
theta, phi = np.meshgrid(theta, phi)
x = (c + a * np.cos(theta)) * np.cos(phi)
y = (c + a * np.cos(theta)) * np.sin(phi)
z = a * np.sin(theta)
x = (R + r * np.cos(theta)) * np.cos(phi)
y = (R + r * np.cos(theta)) * np.sin(phi)
z = r * np.sin(theta)
return x, y, z
def torus_line(n, c, a, u, v):
def torus_line(n, r, R, u, v):
phi = np.linspace(0, 2 * np.pi * u, n)
theta = (v / u) * phi
theta, phi = theta % (2 * np.pi), phi % (2 * np.pi)
x = (c + a * np.cos(theta)) * np.cos(phi)
y = (c + a * np.cos(theta)) * np.sin(phi)
z = a * np.sin(theta)
x = (R + r * np.cos(theta)) * np.cos(phi)
y = (R + r * np.cos(theta)) * np.sin(phi)
z = r * np.sin(theta)
line = np.column_stack([x, y, z])
x, y, z = c * np.cos(phi), c * np.sin(phi), 0 * phi
x, y, z = R * np.cos(phi), R * np.sin(phi), 0 * phi
norm = line - np.column_stack([x, y, z])
return line, norm
def torus_points(c, a, u, v, k):
def torus_points(r, R, u, v, k):
phi = np.linspace(0, 2 * np.pi * u, k + 1)
theta = (v / u) * phi
theta, phi = theta % (2 * np.pi), phi % (2 * np.pi)
x = (c + a * np.cos(theta)) * np.cos(phi)
y = (c + a * np.cos(theta)) * np.sin(phi)
z = a * np.sin(theta)
x = (R + r * np.cos(theta)) * np.cos(phi)
y = (R + r * np.cos(theta)) * np.sin(phi)
z = r * np.sin(theta)
return x, y, z
@ -54,18 +54,24 @@ def get_obscure_segs(arr):
def main():
fig = plt.figure(figsize=(10, 10), constrained_layout=True)
ax = fig.add_subplot(projection="3d")
ax.set_zlim(-2, 2)
ax.set_axis_off()
ax.set_box_aspect(aspect=(1, 1, 1))
alpha = np.sqrt(3) * 2 / 5
size = 5
azim, elev = 0, 40
c, a = 2, 1
u, v = 4, 1
k = 10
R, r = size, size * (1 - alpha) / alpha
u, v = 5, 2
k = 20
print(alpha, r / (r + R))
ax.set_zlim(-(r + R), r + R)
ax.view_init(elev, azim)
ax.plot_surface(
*torus_mesh(100, c, a), rcount=200, ccount=200, color="white", alpha=0.4
*torus_mesh(100, r, R), rcount=200, ccount=200, color="white", alpha=0.4
)
# Calculate when surface is facing or away and apply dotted vs solid lines.
@ -73,7 +79,7 @@ def main():
camera_vec = np.array(
[np.sin(el) * np.cos(az), np.sin(el) * np.sin(az), np.cos(el)]
)
line, norm = torus_line(500, c, a, u, v)
line, norm = torus_line(500, r, R, u, v)
cond = np.dot(norm, camera_vec) >= 0
bounds = get_obscure_segs(cond)
@ -94,9 +100,10 @@ def main():
ax.plot(lx, ly, lz, color="blue", linewidth=2, linestyle=style)
# Display points.
ax.scatter(*torus_points(c, a, u, v, k), color="purple", s=50)
ax.scatter(*torus_points(r, R, u, v, k), color="purple", s=50)
# ax.plot(*torus_line(200, 2, 1, 2, 1), color="orange", linewidth=3)
plt.savefig(f"Torus - N{k}({u},{v}).png", bbox_inches="tight")
plt.show()
# plt.savefig(f"Torus - N{k}({u},{v}).png", bbox_inches="tight")
if __name__ == "__main__":

330
scripts/torus_mesh.py Normal file
View File

@ -0,0 +1,330 @@
import argparse, numpy as np, os
from stl.stl import ASCII
from stl import mesh
from squish import DomainParams, Simulation, ordered
from squish.common import OUTPUT_DIR
from scipy.spatial import Voronoi
SUBDIVISION_AMOUNT = 8
def not_in_order(i, j):
if i < j:
return j - i != 1
else:
return i - j == 1
def centroid(site, verts):
area, c = 0, 0
v = verts - site
# print(v)
for i in range(len(v)):
x, y = v[i], v[(i + 1) % len(v)]
a = np.cross(x, y)
area += a
c += a * (x + y)
return c / (3 * area) + site
def flat_sheet(k):
x = np.linspace(-np.pi, np.pi, 101)
x = np.transpose([np.tile(a, len(a)), np.repeat(a, len(a))])
all_verts = np.hstack((a, np.zeros((len(a), 1))))
xx = np.array([[x for x in range(101 * 100) if (x - 100) % 101 > 0]]).T
b = np.hstack((xx, xx + 1, xx + 101))
c = np.hstack((xx + 1, xx + 102, xx + 101))
all_faces = np.concatenate((b, c))
return all_verts, all_faces
def torus_transform(v, R, r):
new_verts = np.empty(v.shape)
# Rotate by 90 degrees, and then shift to range [0, 2\pi]
v[:, :2] = np.matmul(np.array([[0, -1], [1, 0]]), v[:, :2].T).T
v[:, :2] = v[:, :2] % (2 * np.pi)
# v[:, 0], v[:, 1] = v[:, 1], v[:, 0]
new_verts[:, 0] = (R + r * np.cos(v[:, 1])) * np.cos(v[:, 0])
new_verts[:, 1] = (R + r * np.cos(v[:, 1])) * np.sin(v[:, 0])
new_verts[:, 2] = r * np.sin(v[:, 1])
return new_verts
def flat_region(c, verts, height):
m = len(verts)
centers = np.hstack((np.array([c, c]), np.array([[height], [0]])))
v_top = np.hstack((verts, height * np.ones((m, 1))))
v_bot = np.hstack((verts, np.zeros((m, 1))))
v = np.concatenate((centers, v_top, v_bot))
vi = np.atleast_2d(np.arange(m, dtype=int)).T
cent_top, cent_bot = np.zeros((m, 1), dtype=int), np.ones((m, 1), dtype=int)
vert_top_m, vert_top_p = vi + 2, (vi + 1) % m + 2
vert_bot_m, vert_bot_p = vi + m + 2, (vi + 1) % m + m + 2
top_face = np.hstack((cent_top, vert_top_m, vert_top_p))
bot_face = np.hstack((cent_bot, vert_bot_p, vert_bot_m))
f = np.concatenate((top_face, bot_face))
return v, f, 2
def torus_region(c, verts, height):
sd = SUBDIVISION_AMOUNT
d = verts - c
m = len(verts)
# 1 + 6 + 12 ... verts, 1 + 3 + 5 ... faces.
v, f = (
np.empty((1 + m * sd * (sd - 1) // 2, 2)),
np.empty((m * (sd - 1) ** 2, 3), dtype=int),
)
v[0] *= 0
# Vertices are ordered by rings going out from the center.
v_inds = np.arange(-1, sd)
v_inds = 1 + m * v_inds * (v_inds + 1) // 2
v_inds[0] = 0
for i in range(1, sd):
fj, fk = m * (i - 1) ** 2, m * i ** 2
region_verts = [
np.linspace(i * d[j] / (sd - 1), i * d[(j + 1) % m] / (sd - 1), i + 1)[:-1]
for j in range(m)
]
v[v_inds[i] : v_inds[i + 1]] = np.concatenate(region_verts)
faces = []
for j in range(m):
# v_off_inds = v_inds + j * np.arange(sd + 1)
r1, r2 = (
(j * (i - 1) + np.arange(i + 1)) % (m * (i - 1)) + v_inds[i - 1],
(j * i + np.arange(i + 2)) % (m * i) + v_inds[i],
)
r1, r2 = np.atleast_2d(r1).T, np.atleast_2d(r2).T
faces.append(np.hstack((r1[:-1], r2[:-2], r2[1:-1])))
faces.append(np.hstack((r1[:-2], r2[1:-2], r1[1:-1])))
f[fj:fk] = np.concatenate(faces)
v += c
v = np.hstack((v, height * np.ones((len(v), 1))))
return v, f, v_inds[sd - 1]
centers = np.array([[c[0], c[1], heights[i]]])
v_top = np.hstack((verts, heights[i] * np.ones((len(verts), 1))))
face_verts = np.concatenate((centers, v_top))
def main():
parser = argparse.ArgumentParser(description="Generates 3D model for equilibrium.")
parser.add_argument(
"sims_path", metavar="sim_dir", help="simulation to obtain equilibrium from"
)
parser.add_argument("frame_num", metavar="FRAME_NUM", type=int, help="frame number")
parser.add_argument("size", metavar="SIZE", type=int, help="width in mm")
parser.add_argument(
"--torus", dest="torus", action="store_true", help="generates torus model"
)
args = parser.parse_args()
torus = args.torus
size = args.size
# Get desired frame and load.
sim, frames = Simulation.load(args.sims_path)
frames = list(frames)
# num = 100
# nums = [838. 348. 14. 664, 725, 974]
# frames.sort(key=lambda x: x["energy"])
# frame = frames[-32]
# for i, f in enumerate(frames):
# if i == 858:
frame = frames[args.frame_num]
n, w, h = frame["domain"][0], frame["domain"][1], frame["domain"][2]
frame = sim.energy.mode(*frame["domain"], frame["arr"])
# Set up size and scaling variables.
dim = np.array([w, h])
vor = frame.vor_data
alpha = w / h
if torus:
r = alpha * size / 2
R = size / 2 - r
height_bounds = (0.5 * r, 1.25 * r)
scale = np.array([2 * np.pi, 2 * np.pi])
else:
height_bounds = (size / 10, 0.75 * size)
scale = np.array([alpha * size, size])
# Rescale and translate domain.
points = (vor.points - dim / 2) * scale / dim
vertices = (vor.vertices - dim / 2) * scale / dim
# Obtain height scaling.
heights = frame.stats["site_energies"] - ordered.e_hex(sim.domain)
heights -= np.min(heights)
heights *= (height_bounds[1] - height_bounds[0]) / np.max(heights)
heights += height_bounds[0]
# Prepare oriented regions and site_vert list
sites = points[:n]
regions = []
for i, x in enumerate(vor.point_region[:n]):
region = vor.regions[x]
x = sites[i]
p, q = vertices[region[0]] - x, vertices[region[1]] - x
regions.append(region if np.cross(p, q) >= 0 else region[::-1])
site_verts = []
for region in regions:
site_verts.append(vertices[region])
all_verts, all_faces = [], []
offsets = np.empty((n,), dtype=int)
for i, verts in enumerate(site_verts):
c = centroid(sites[i], verts)
if torus:
face_verts, faces, offset = torus_region(c, verts, heights[i])
else:
face_verts, faces, offset = flat_region(c, verts, heights[i])
old_len = len(all_verts)
if i == 0:
all_verts, all_faces = face_verts, faces
else:
all_verts = np.concatenate((all_verts, face_verts))
all_faces = np.concatenate((all_faces, faces + old_len))
offsets[i] = old_len + offset
# Merge regions
for i, x in enumerate(vor.ridge_points):
x, y = x[0], x[1] # Site indices.
no_x, no_y = x >= sim.domain.n, y >= sim.domain.n
if no_x and no_y:
continue
adj_verts = vor.ridge_vertices[i]
v1, v2 = vertices[adj_verts[0]], vertices[adj_verts[1]]
bot_face, top_face = None, None
if torus:
sd = SUBDIVISION_AMOUNT - 1
if no_x:
x, y = y, x
y = y % n
v1_x_ind = regions[x].index(adj_verts[0])
v2_x_ind = regions[x].index(adj_verts[1])
# Need to find matching vertices
v1, v2 = v1 % (2 * np.pi), v2 % (2 * np.pi)
y_verts = vertices[regions[y]] % (2 * np.pi)
v1_y_ind = np.argmin(np.linalg.norm(y_verts - v1, axis=1))
v2_y_ind = np.argmin(np.linalg.norm(y_verts - v2, axis=1))
# We want x lower than y.
off_x, off_y = offsets[x], offsets[y]
if heights[x] > heights[y]:
v1_x_ind, v1_y_ind = v1_y_ind, v1_x_ind
v2_x_ind, v2_y_ind = v2_y_ind, v2_x_ind
off_x, off_y = off_y, off_x
if not_in_order(v1_x_ind, v2_x_ind):
v1_x_ind, v2_x_ind = v2_x_ind, v1_x_ind
if not_in_order(v1_y_ind, v2_y_ind):
v1_y_ind, v2_y_ind = v2_y_ind, v1_y_ind
sds = np.atleast_2d(np.arange(sd)).T
xm, ym = sd * v1_x_ind + sds, sd * v1_y_ind + sds
xp, yp = xm + 1, ym + 1
xp[-1], yp[-1] = sd * v2_x_ind, sd * v2_y_ind
xm += off_x
xp += off_x
ym += off_y
yp += off_y
bot_face = np.hstack((xm, ym[::-1], xp))
top_face = np.hstack((xm, yp[::-1], ym[::-1]))
else:
if no_x ^ no_y:
if no_x:
x = y
m = len(regions[x])
v1_ind = regions[x].index(adj_verts[0])
v2_ind = regions[x].index(adj_verts[1])
if not_in_order(v1_ind, v2_ind):
v1_ind, v2_ind = v2_ind, v1_ind
v1_ind += offsets[x]
v2_ind += offsets[x]
bot_face = np.array([[v1_ind, m + v1_ind, m + v2_ind]], dtype=int)
top_face = np.array([[v1_ind, m + v2_ind, v2_ind]], dtype=int)
else:
v1_x_ind = regions[x].index(adj_verts[0])
v2_x_ind = regions[x].index(adj_verts[1])
v1_y_ind = regions[y].index(adj_verts[0])
v2_y_ind = regions[y].index(adj_verts[1])
# We want x lower than y.
off_x, off_y = offsets[x], offsets[y]
if heights[x] > heights[y]:
v1_x_ind, v1_y_ind = v1_y_ind, v1_x_ind
v2_x_ind, v2_y_ind = v2_y_ind, v2_x_ind
off_x, off_y = off_y, off_x
if not_in_order(v1_x_ind, v2_x_ind):
v1_x_ind, v2_x_ind = v2_x_ind, v1_x_ind
if not_in_order(v1_y_ind, v2_y_ind):
v1_y_ind, v2_y_ind = v2_y_ind, v1_y_ind
v1_x_ind += off_x
v2_x_ind += off_x
v1_y_ind += off_y
v2_y_ind += off_y
bot_face = np.array([[v1_x_ind, v1_y_ind, v2_x_ind]], dtype=int)
top_face = np.array([[v1_x_ind, v2_y_ind, v1_y_ind]], dtype=int)
all_faces = np.concatenate((all_faces, bot_face, top_face))
if torus:
all_verts = torus_transform(all_verts, R, all_verts[:, 2])
# Output into mesh
surf = mesh.Mesh(np.zeros(all_faces.shape[0], dtype=mesh.Mesh.dtype))
for i, f in enumerate(all_faces):
for j in range(3):
surf.vectors[i][j] = all_verts[f[j], :]
m_type = "Torus" if torus else "Flat"
out = OUTPUT_DIR / f"N{n} - {m_type} - {args.frame_num}.stl"
# temp = f"n{n}{m_type.lower()}{num}.stl"
surf.save(out)
# os.rename(temp, out)
print(f"Wrote to {out}.")
# n, alpha = 20, np.sqrt(3) * 2 / 5
# u, v = 5, 2
# domain = DomainParams(n, alpha, 1, 1)
# size = 1
# R, r = size, size * (1 - alpha) / alpha
# sites = ordered.sites(domain, (u, v))
if __name__ == "__main__":
np.seterr(divide="ignore")
main()

View File

@ -43,7 +43,7 @@ def main():
plt.rcParams.update(RC_SETTINGS)
fig = plt.figure(figsize=(15, 15))
fig = plt.figure(figsize=(20, 9))
gs = fig.add_gridspec(1, 1)
ax = fig.add_subplot(gs[0])
@ -78,11 +78,11 @@ def main():
100 * (min_order - min_disorder), all_disorder_count, label=f"N={domain.n}"
)
ax.set_ylabel("Perecent of Disordered Equilibria")
ax.set_ylabel("POD")
ax.set_xlabel(r"VEE Difference $\left[\times 10^{2}\right]$")
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
ax.set_ylim(48, 102)
ax.set_yticks(np.arange(50, 101, 5))
ax.set_yticks(np.arange(50, 101, 10))
ax.grid(zorder=0)
ax.legend()

View File

@ -9,7 +9,7 @@ from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable
import numpy as np, os, math
from matplotlib.ticker import MaxNLocator, FormatStrFormatter
from scipy.spatial import Voronoi, voronoi_plot_2d
from scipy.spatial import Voronoi
from multiprocessing import Pool, cpu_count
from .common import DomainParams, OUTPUT_DIR
@ -125,37 +125,13 @@ class Diagram:
self.diagrams = np.atleast_2d(diagrams)
self.cumulative = cumulative
def generate_frame(self, frame: int, mode: str, fol: str, name: str = None) -> None:
def generate_frame(
self, fig: Figure, frame: int, mode: str, fol: str, name: str = None
) -> None:
if mode not in ["save", "open"]:
raise ValueError("Not a valid mode for diagrams!")
shape = self.diagrams.shape
plt.rcParams.update(
{
"axes.titlesize": 45,
"axes.labelsize": 45,
"xtick.labelsize": 40,
"ytick.labelsize": 40,
"xtick.major.width": 2,
"ytick.major.width": 2,
"xtick.major.size": 5,
"ytick.major.size": 5,
"xtick.minor.width": 1,
"ytick.minor.width": 1,
"xtick.minor.size": 3,
"ytick.minor.size": 3,
"legend.fontsize": 40,
"lines.linewidth": 3,
"font.family": "cm",
"font.size": 40,
"text.usetex": True,
"text.latex.preamble": r"\usepackage{amsmath}",
"figure.constrained_layout.use": True,
}
)
fig = plt.figure(figsize=(shape[1] * 15, shape[0] * 15))
gs = fig.add_gridspec(shape[0], shape[1])
# fig, axes = plt.subplots(*shape, figsize=(shape[1] * 15, shape[0] * 15))
@ -178,29 +154,28 @@ class Diagram:
if mode == "save":
plt.savefig(self.sim.path / fol / name)
plt.close(fig)
fig.clear()
elif mode == "show":
plt.show()
def voronoi_plot(self, i: int, ax: AxesSubplot) -> None:
domain = self.sim.domains[i]
n, w, h = domain.n, domain.w, domain.h
flip = w <= h
scale = 1.5
area = n <= 50
e_hex = ordered.e_hex(domain)
line_color, point_color = "white", "lightseagreen"
# Make color map axis.
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="5%", pad=0.05)
voronoi_plot_2d(
self.sim.voronois[i], ax, show_vertices=False, show_points=False
)
vor = self.sim.voronois[i]
# Mark location axis with 3 ticks.
ax.set_xticks([0, w / 2, w])
ax.set_yticks([0, h / 2, h])
ax.set_xticks(np.round([0, w / 2, w], 2))
ax.set_yticks(np.round([0, h / 2, h], 2))
# Obtain site energies, and make color map.
energies = self.sim.stats[i]["site_energies"][:n]
@ -208,74 +183,77 @@ class Diagram:
mapper = cm.ScalarMappable(norm=norm, cmap=cm.magma)
cbar = plt.colorbar(mapper, cax=cax, ticks=np.linspace(-2, 2, 5))
SYMM = []
if flip:
dup = int((h // w + 1) // 2 + 1)
for i in range(-dup, dup + 1):
SYMM += [[i, -1], [i, 0], [i, 1]]
else:
dup = int((w // h + 1) // 2 + 1)
for i in range(-dup, dup + 1):
SYMM += [[-1, i], [0, i], [1, i]]
SYMM = np.array(SYMM) * np.array([w, h])
regions = [vor.regions[x] for x in vor.point_region[:n]]
edges = np.array([len(r) for r in regions])
# Fill polygons with energy colormap.
for j, p in enumerate(self.sim.voronois[i].point_region):
region = self.sim.voronois[i].regions[p]
if not -1 in region:
polygon = [self.sim.voronois[i].vertices[k] for k in region]
ax.fill(
*zip(*polygon),
color=mapper.to_rgba(energies[j % n] - e_hex),
zorder=0,
sizes = [40 - n / 100, 200 - n / 100, 40 - n / 100, 200 - n / 100, 40 - n / 100]
markers = [".", "p", ".", "*", "."]
for j in range(5):
sites = vor.points[np.argwhere(edges == j + 4)]
if len(sites) == 0:
continue
for s in SYMM:
ax.scatter(
*(sites + s).T,
marker=markers[j],
s=sizes[j],
color=point_color,
zorder=1,
)
line_color, point_color = "white", "lightseagreen"
polygons = []
for region in regions:
polygon = np.empty((2 * len(SYMM), len(region)), dtype=float)
for i, s in enumerate(SYMM):
polygon[i * 2 : (i + 1) * 2, :] = (vor.vertices[region] + s).T
polygons.append(polygon)
colors = [mapper.to_rgba(energies[i] - e_hex) for i in range(n)]
for col, poly_set in zip(colors, polygons):
ax.fill(*poly_set, color=col, edgecolor="black", zorder=0)
ax.axis("equal")
# Periodic border
ax.plot([-w, 2 * w], [0, 0], line_color, linewidth="4")
ax.plot([-w, 2 * w], [h, h], line_color, linewidth="4")
ax.plot([0, 0], [-h, 2 * h], line_color, linewidth="4")
ax.plot([w, w], [-h, 2 * h], line_color, linewidth="4")
ax.axis("equal")
ax.set_xlim([(1 - 0.85 * scale) * w / 2, (1 + 0.85 * scale) * w / 2])
ax.set_ylim([(1 - scale) * h / 2, (1 + scale) * h / 2])
if flip:
bot, top = (1 - scale) * h / 2, (1 + scale) * h / 2
left, right = w / 2 - 0.88 * (top - bot) / 2, w / 2 + 0.88 * (top - bot) / 2
ax.plot([left, right], [0, 0], line_color, linewidth="4")
ax.plot([left, right], [h, h], line_color, linewidth="4")
for i in range(-dup, dup + 2):
ax.plot([i * w, i * w], [-h, 2 * h], line_color, linewidth="4")
else:
left, right = (1 - 0.85 * scale) * w / 2, (1 + 0.85 * scale) * w / 2
bot, top = (h / 2 - (scale * w) / 2, h / 2 + (scale * w) / 2)
ax.plot([0, 0], [bot, top], line_color, linewidth="4")
ax.plot([w, w], [bot, top], line_color, linewidth="4")
for i in range(-dup, dup + 2):
ax.plot([-w, 2 * w], [i * h, i * h], line_color, linewidth="4")
ax.set_xlim(left, right)
ax.set_ylim(bot, top)
ax.set_title("Voronoi Tessellation")
# Marking sites and defects.
defects = {5: {"x": [], "y": []}, 7: {"x": [], "y": []}}
for j in range(n):
for s in SYMM:
vec = self.sim.voronois[i].points[j] + s * self.sim.domains[i].dim
if area:
txt = ax.text(
*vec, str(round(self.sim.stats[i]["site_areas"][j], 3))
)
txt.set_clip_on(True)
if self.sim.stats[i]["site_edge_count"][j] == 5:
defects[5]["x"].append(vec[0])
defects[5]["y"].append(vec[1])
elif self.sim.stats[i]["site_edge_count"][j] == 7:
defects[7]["x"].append(vec[0])
defects[7]["y"].append(vec[1])
else:
ax.scatter(
vec[0], vec[1], s=(40 - n / 100), color=point_color, zorder=1
)
ax.scatter(
defects[5]["x"],
defects[5]["y"],
marker="p",
s=(200 - n / 100),
color=point_color,
zorder=1,
)
ax.scatter(
defects[7]["x"],
defects[7]["y"],
marker="*",
s=(200 - n / 100),
color=point_color,
zorder=1,
)
# Make VEE text in top left.
props = dict(boxstyle="round", facecolor="white", alpha=0.8, zorder=20)
ax.text(
0.065,
0.935,
f"VEE: {round(sum(energies)/n - e_hex, 8)}",
f"VEE: {sum(energies)/n - e_hex: .8f}",
transform=ax.transAxes,
verticalalignment="top",
bbox=props,
@ -283,16 +261,28 @@ class Diagram:
def energy_plot(self, i: int, ax: AxesSubplot) -> None:
ax.set_xlim([0, len(self.sim)])
e_hex = ordered.e_hex(self.sim.domains[i])
energies = self.sim.energies[: (i + 1)]
ax.plot(list(range(i + 1)), energies)
ax.title.set_text("Energy vs. Time")
vees = np.array(self.sim.energies) / self.sim.domains[i].n - e_hex
ax.plot(list(range(len(vees))), vees)
ax.scatter(
i,
vees[i],
s=250,
facecolors="none",
edgecolors="C6",
linewidth=4,
zorder=100,
)
# ax.title.set_text("VEE")
# max_value = round(self.sim[0].energy)
# min_value = round(self.sim[-1].energy)
# diff = max_value-min_value
# ax.set_yticks(np.arange(int(min_value-diff/5), int(max_value+diff/5), diff/25))
ax.set_xlabel("Iterations")
ax.set_ylabel("Energy")
ax.set_xlabel("Frame")
ax.set_ylabel("VEE")
ax.grid()
def site_areas_plot(self, i: int, ax: AxesSubplot) -> None:
@ -424,11 +414,8 @@ class Diagram:
self.sim.path.mkdir(exist_ok=True)
(self.sim.path / fol).mkdir(exist_ok=True)
combo_list = []
for i in range(cpu_count()):
start, end = (
int(i * len(frames) / cpu_count()),
int((i + 1) * len(frames) / cpu_count()),
)
for i in range(1):
start, end = (int(i * len(frames) / 1), int((i + 1) * len(frames) / 1))
new_dia = Diagram(None, self.diagrams, self.cumulative)
new_dia.sim = self.sim.slice(frames[start:end])
combo_list.append(
@ -448,10 +435,10 @@ class Diagram:
if mode not in ["use_all", "sample"]:
raise ValueError("Not a valid mode for videos!")
fps = 30
if mode == "use_all":
frames = list(range(len(self.sim)))
elif mode == "sample":
fps = 30
if len(self.sim) < fps * time:
frames = list(range(len(self.sim)))
fps = len(self.sim) / time
@ -481,8 +468,36 @@ class Diagram:
def render_frame_range(combo: Tuple[Diagram, str, int, int, int]) -> None:
self, fol, offset, length, num_frames = combo
plt.rcParams.update(
{
"axes.titlesize": 45,
"axes.labelsize": 45,
"xtick.labelsize": 40,
"ytick.labelsize": 40,
"xtick.major.width": 2,
"ytick.major.width": 2,
"xtick.major.size": 5,
"ytick.major.size": 5,
"xtick.minor.width": 1,
"ytick.minor.width": 1,
"xtick.minor.size": 3,
"ytick.minor.size": 3,
"legend.fontsize": 40,
"lines.linewidth": 3,
"font.family": "cm",
"font.size": 40,
"text.usetex": True,
"text.latex.preamble": r"\usepackage{amsmath}",
"figure.constrained_layout.use": True,
}
)
# Generate figure here and pass in to prevent matplotlib memory leak.
shape = self.diagrams.shape
fig = plt.figure(figsize=(shape[1] * 15, shape[0] * 15))
for i in range(length):
self.generate_frame(i, "save", fol, f"img{i+offset:05}.png")
self.generate_frame(fig, i, "save", fol, f"img{i+offset:05}.png")
i = len(list((self.sim.path / fol).iterdir()))
hashes = int(21 * i / num_frames)
print(
@ -491,3 +506,4 @@ def render_frame_range(combo: Tuple[Diagram, str, int, int, int]) -> None:
flush=True,
end="\r",
)
plt.close(fig)

View File

@ -14,6 +14,13 @@ def e_hex(domain: DomainParams) -> float:
return 2 - 2 * domain.r * (2 * pi) * R_HEX + 2 * pi * domain.r ** 2
def toroidal_distance(domain: DomainParams, x: np.ndarray, y: np.ndarray) -> float:
dim = np.array([domain.w, domain.h])
absdist = np.abs(y - x)
wrap = dim * (absdist >= dim / 2)
return np.linalg.norm(wrap - absdist, axis=1)
def configurations(domain: DomainParams) -> List[Config]:
n = domain.n
coprimes, valid = [], []
@ -41,30 +48,37 @@ def configurations(domain: DomainParams) -> List[Config]:
def get_config_generators(
domain: DomainParams, config: Config
) -> Tuple[Config, Config]:
# x = sites(domain, config)
# x = x[x[:, 1] <= (domain.h / 2)]
# mag_x = toroidal_distance(domain, x, np.zeros(x.shape))
# x = x[np.argsort(mag_x)]
# print(x)
# return [tuple(x[1]), tuple(x[2])]
n, w, h = domain.n, domain.w, domain.h
q1 = sites(domain, config)[1:]
all_sites = np.concatenate(
(q1, q1 - np.array([w, 0]), q1 - np.array([0, h]), q1 - domain.dim)
x = sites(domain, config)[1:] # Remove 0
# Concatenate quadrants 1, 2 and 4.
x = np.concatenate(
(x, x - np.array([w, 0]), x - np.array([0, h]), x - np.array([w, h]))
)
# Sort sites by magnitude and smallest.
all_sites = sorted(list(all_sites), key=lambda x: np.linalg.norm(x))
v = all_sites[0] # Smallest vector set to v.
all_sites = np.array(all_sites[1:]) # Remove v from search set.
# Reduce search space
# x = x[(np.abs(x[:, 0]) <= (domain.w / 2)) * (np.abs(x[:, 1]) <= (domain.h / 2))]
mag_x = np.linalg.norm(x, axis=1)
x = x[np.argsort(mag_x)] # Sort by magnitude
v = x[0]
x = x[1:] # Remove v from search set.
# Checking 0 < ax + by < v*v to make the sites are within the region.
tol = 1e-3
vdot = np.matmul(all_sites, v)
in_box = all_sites[np.where((-tol <= vdot) & (vdot <= (v.dot(v) + tol)))[0]]
in_box = np.expand_dims(in_box, 0).swapaxes(
0, 1
) # Used for the next step, getting site*site
tol = 1e-8
vdot = np.matmul(x, v)
x = x[(-tol <= vdot) * (vdot <= v.dot(v) + tol)]
mag_x = np.linalg.norm(x, axis=1)
x = x[np.argsort(mag_x)]
v2 = in_box[
np.argmin(np.squeeze(np.matmul(in_box, in_box.transpose(0, 2, 1))))
].flatten()
v2 = x[0]
if np.all(v == v2):
print(v, v2, n, w, h, config)
return tuple(v), tuple(v2)
@ -105,7 +119,8 @@ def avg_rp(d: float, l: float) -> float:
def circumcenter(v: numpy.ndarray, w: numpy.ndarray) -> numpy.ndarray:
det = 1 / (2 * rot(v).dot(w))
if rot(v).dot(w) == 0:
print(v, w)
pass
# print(v, w)
v2, w2 = v.dot(v), w.dot(w)
c = np.empty((2,))
c[0], c[1] = w[1] * v2 - v[1] * w2, -w[0] * v2 + v[0] * w2

View File

@ -217,7 +217,6 @@ class Flow(Simulation):
log: bool,
log_steps: int,
new_sites: Optional[numpy.ndarray] = None,
newton: bool = False,
) -> None:
if log:
print(f"Find - {self.domain}", flush=True)
@ -242,11 +241,11 @@ class Flow(Simulation):
new_frame = self.energy.mode(*self.domain, frame.add_sites(change))
end = timer()
grad_norm = np.linalg.norm(grad)
grad_norm = np.linalg.norm(grad) / (self.domain.n ** 0.5)
if self.adaptive:
error = change - grad * self.step_size
tol = 10 ** min(-5, 2.3 * log10(grad_norm))
tol = 10 ** min(-3, -2 + log10(grad_norm))
# tol = 10 ** -10
self.step_size *= (tol / np.linalg.norm(error)) ** 0.5
@ -393,10 +392,7 @@ class Shrink(Simulation):
) -> None:
super().__init__(domain, energy, name=name)
self.step_size, self.thres, self.adaptive = step_size, thres, adaptive
self.delta, self.stop_width = (
self.domain.w * delta / 100,
self.domain.w * stop_width,
)
self.delta, self.stop_width = delta, stop_width
@property
def initial_data(self) -> Dict:
@ -425,8 +421,19 @@ class Shrink(Simulation):
self.save(self.initial_data, True)
width = self.domain.w if len(self.frames) == 0 else self.frames[-1].w
if len(self.frames) != 0:
new_sites = self.frames[-1].site_arr
width = self.frames[-1].w - self.delta
else:
width = self.domain.w
i = 0
while width >= self.stop_width:
if delta < 0:
cond = width <= self.stop_width
if delta > 0:
cond = width <= self.stop_width
while cond:
# Get to equilibrium.
new_domain = DomainParams(
self.domain.n, width, self.domain.h, self.domain.r