125 for i
in range(n_max):
126 for j
in range(n_max):
128 if mask_outside[i, j]:
129 ax.add_patch(Rectangle((n - 0.5, m - 0.5), 1, 1,
130 facecolor=_OUTSIDE, edgecolor=
"white",
131 lw=1.5, hatch=
"///", zorder=1))
133 if np.isnan(grid[i, j]):
134 ax.add_patch(Rectangle((n - 0.5, m - 0.5), 1, 1,
135 facecolor=
"white", edgecolor=
"white",
138 rgba = cmap(norm(grid[i, j]))
139 ax.add_patch(Rectangle((n - 0.5, m - 0.5), 1, 1, facecolor=rgba,
140 edgecolor=
"white", lw=1.5, zorder=1))
141 ax.text(n, m, fmt(grid[i, j]), ha=
"center", va=
"center",
144 ax.set_xlim(0.5, n_max + 0.5)
145 ax.set_ylim(0.5, n_max + 0.5)
146 ax.set_xticks(range(1, n_max + 1))
147 ax.set_yticks(range(1, n_max + 1))
148 ax.set_aspect(
"equal")
149 for side
in (
"top",
"right"):
150 ax.spines[side].set_visible(
False)
151 for side
in (
"left",
"bottom"):
152 ax.spines[side].set_color(
"#b4b4b4")
156 """The two-tails/one-head convention, drawn once."""
159 ax.set_aspect(
"equal")
162 def reaction(y, t1, t2, head, colour, caption):
163 for (cx, cy), lab
in (((1.5, y + 1.1), t1), ((1.5, y - 1.1), t2)):
164 ax.add_patch(Circle((cx, cy), 0.72, facecolor=
"white",
165 edgecolor=
"#3f3f3f", lw=_LW_THIN, zorder=3))
166 ax.text(cx, cy, lab, ha=
"center", va=
"center", fontsize=_PLOT_FONTSIZE,
167 color=_INK, zorder=4)
168 ax.add_patch(Rectangle((4.55, y - 0.45), 0.9, 0.9, facecolor=colour,
169 edgecolor=
"none", zorder=3))
170 for cy
in (y + 1.1, y - 1.1):
171 ax.add_patch(FancyArrowPatch((2.25, cy), (4.5, y),
172 arrowstyle=
"-|>", mutation_scale=18,
173 lw=_LW_THIN, color=colour, zorder=2))
174 ax.add_patch(FancyArrowPatch((5.5, y), (7.55, y), arrowstyle=
"-|>",
175 mutation_scale=18, lw=_LW, color=colour,
177 ax.add_patch(Circle((8.3, y), 0.72, facecolor=
"white",
178 edgecolor=
"#3f3f3f", lw=_LW_THIN, zorder=3))
179 ax.text(8.3, y, head, ha=
"center", va=
"center", fontsize=_PLOT_FONTSIZE,
180 color=_INK, zorder=4)
181 ax.text(5.0, y - 1.85, caption, ha=
"center", va=
"top",
182 fontsize=_PLOT_FONTSIZE, color=_GREY_TEXT)
184 reaction(7.0,
"$n$",
"$m$",
"$n{+}m$",
"#256abf",
"coalescence")
185 reaction(2.4,
"$n$",
"$m$",
"$n{-}m$",
"#d03b3b",
"annihilation")
189 inp =
InputData(I=200, V=200, physics_option=
"full_CD_fission")
191 G = to_networkx(rag, n_max=n_max, include_binary=
True)
194 coal = [h
for h
in hyper
if h[2] ==
"coalescence"]
195 annih = [h
for h
in hyper
if h[2] ==
"annihilation"]
198 c_grid = np.full((n_max, n_max), np.nan)
199 for tails, head, _, _
in coal:
200 sizes = sorted(
_key(t)[1]
for t
in tails)
201 n, m = sizes[0], sizes[-1]
203 if n <= n_max
and m <= n_max:
204 c_grid[m - 1, n - 1] = hn
205 c_grid[n - 1, m - 1] = hn
207 c_outside = np.zeros((n_max, n_max), bool)
208 for i
in range(n_max):
209 for j
in range(n_max):
210 if np.isnan(c_grid[i, j])
and (i + 1) + (j + 1) > n_max:
211 c_outside[i, j] =
True
214 a_grid = np.full((n_max, n_max), np.nan)
215 for tails, head, _, _
in annih:
216 vac = [t
for t
in tails
if _key(t)[0] ==
"bulk"]
217 sia = [t
for t
in tails
if _key(t)[0]
in (
"bulk-111",
"bulk-100")]
218 if not vac
or not sia:
220 nv, ns =
_key(vac[0])[1],
_key(sia[0])[1]
221 if nv <= n_max
and ns <= n_max:
222 a_grid[ns - 1, nv - 1] = nv - ns
223 a_outside = np.zeros((n_max, n_max), bool)
225 fig = plt.figure(figsize=(26.0, 10.5))
226 gs = fig.add_gridspec(1, 3, width_ratios=[0.85, 1.0, 1.0], wspace=0.28)
227 ax0, ax1, ax2 = (fig.add_subplot(gs[0, k])
for k
in range(3))
231 lo, hi = np.nanmin(c_grid), np.nanmax(c_grid)
232 n_seq = Normalize(vmin=lo, vmax=hi)
233 _draw_matrix(ax1, c_grid, c_outside, n_max, _SEQ, n_seq,
234 lambda v: f
"{int(v)}")
235 ax1.set_xlabel(
"$n$")
236 ax1.set_ylabel(
"$m$")
237 fig.colorbar(plt.cm.ScalarMappable(norm=n_seq, cmap=_SEQ), ax=ax1,
238 fraction=0.046, pad=0.03,
239 label=
"product size $n{+}m$")
241 amax = np.nanmax(np.abs(a_grid))
242 n_div = TwoSlopeNorm(vmin=-amax, vcenter=0.0, vmax=amax)
243 _draw_matrix(ax2, a_grid, a_outside, n_max, _DIV, n_div,
244 lambda v: f
"{int(abs(v))}" if v
else "0")
245 ax2.set_xlabel(
"vacancy cluster size $n$")
246 ax2.set_ylabel(
"SIA cluster size $m$")
247 cb = fig.colorbar(plt.cm.ScalarMappable(norm=n_div, cmap=_DIV), ax=ax2,
248 fraction=0.046, pad=0.03,
249 label=
"surviving cluster")
250 cb.set_ticks([-amax, 0, amax])
251 cb.set_ticklabels([
"SIA",
"mutual\n(SINK)",
"vacancy"])
253 fams_c = sorted({h[3]
for h
in coal})
254 fams_a = sorted({h[3]
for h
in annih})
255 _wrap =
lambda names:
"\n".join(textwrap.wrap(
", ".join(names), 46))
256 ax1.annotate(
"union over " + str(len(fams_c))
257 +
" families, all with head $n{+}m$:\n"
259 +
"\n\nhatched: pair declared, product outside the domain",
260 xy=(0.5, -0.15), xycoords=
"axes fraction", ha=
"center",
261 va=
"top", fontsize=_PLOT_FONTSIZE, color=_GREY_TEXT,
263 ax2.annotate(
"union over " + str(len(fams_a)) +
" families:\n"
265 xy=(0.5, -0.15), xycoords=
"axes fraction", ha=
"center",
266 va=
"top", fontsize=_PLOT_FONTSIZE, color=_GREY_TEXT,
270 out.parent.mkdir(parents=
True, exist_ok=
True)
271 fig.savefig(out, dpi=_DPI, bbox_inches=
"tight", facecolor=
"white")
272 print(f
"coalescence hyperedges {len(coal)} "
273 f
"annihilation hyperedges {len(annih)} total arcs "
274 f
"{G.number_of_edges()}")
275 print(f
"figure -> {out}")