Source code for circuit_visualize

import time

import matplotlib
import numpy as np
from scipy.spatial import Voronoi

import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection, LineCollection
from matplotlib.colors import Normalize, LogNorm
import matplotlib.animation as animation
import matplotlib.cm as cm

from pyjjasim import Circuit

from pyjjasim.static_problem import StaticConfiguration
from pyjjasim.time_evolution import TimeEvolutionResult

__all__ = ["CircuitPlot", "ConfigPlot", "CircuitMovie", "TimeEvolutionMovie"]


[docs]class CircuitPlot: """Plots a circuit configuration. Allows one to show node quantities, arrow quantities, face quantities and vortices. - Node quantities are shown as colors on the nodes. - Arrow quantities are displayed at the junctions and the length of the arrows is proportional to the quantity value. - Face quantities are shown as colors on the faces. - Vortices are displayed by symbols (concentric rings, vorticity equals nr of rings, color shows sign). Base class used by :py:attr:`circuit_visualize.ConfigPlot`, where one can specify what physical quantities to display in the circuit. Parameters ---------- circuit : :py:attr:`josephson_circuit.Circuit` Object representing circuit. node_data=None : (Nn,) array or None Data associated with nodes in circuit. Can be visualized as colors. If None, node data is not visualized. arrow_data=None : (Nj,) array or None Data associated with junctions in circuit. Can be visualized as arrows. where the length corresponds with the magnitude of the data. If None, arrow data is not visualized. face_data=None : (Nf,) array or None Data associated with faces in circuit. Can be visualized as colors. If None, face data is not visualized. vortex_data=None : (Nf,) int array or None Integer data associated with faces in circuit. Can be visualized as circular symbols. The value of the data equals the number of concentric rings of the symbol. The color shows if it is + or -. If None, vortex data is not visualized. vortex_diameter=0.25 : float Diameter of vortex symbols. vortex_color=(0, 0, 0) : color Color of vortex symbols. anti_vortex_color=(0.8, 0.1, 0.2) : color Color of anti-vortex symbols, whose data is negative. vortex_alpha=1 : float Transparency of vortex symbols. vortex_label="" : str Label given to vortex data in legend. show_grid=True : bool Display a grid at the edges of the graph. grid_width=1 : float Width of lines of grid. grid_color=(0.4, 0.5, 0.6) : color Color of grid. grid_alpha=0.5 : float Transparency of grid. show_colorbar=True : bool Show colorbar mapping face and/or node data to colors. show_legend=True : bool Show legend which includes colormaps, explanation of vortex sybols and an arrow scale. legend_width_fraction=0.2 : float Fraction of the width of the axes (as specified by axis_position) dedicated to the legend; if it is shown. show_axes=True : bool If True, shows axes with the coordinates of the circuit. axis_position=(0.1, 0.1, 0.85, 0.85) : array_like Position of axis in figure (x0, y0, dx, dy, between 0 and 1) arrow_width=0.005 : float Width of arrows. arrow_scale=1 : float Scale-factor for arrows. (length of arrow = arrow_scale * arrow_data) arrow_headwidth=3 : float Width of head of arrows. (see matplotlib.quiver) arrow_headlength=3.5 : float Length of head of arrows. (see matplotlib.quiver) arrow_headaxislength=3 : float Arrow property. (see matplotlib.quiver) arrow_minshaft=1 : float Arrow property. (see matplotlib.quiver) arrow_minlength=1 : float Arrow property. (see matplotlib.quiver) arrow_color=(0.15, 0.3, 0.8) : color Color of arrows. arrow_alpha=1 : float Transparency of arrows. arrow_label="" : str Label given to arrow data in legend. show_nodes=True : bool If True, nodes are displayed as circles. node_diameter=0.25 : float Diameter of nodes. node_face_color=(1,1,1) : color Color of faces of nodes. Only used if there is no node data. node_edge_color=(0, 0, 0) : color Color of edge of nodes. Only used if there is no node data. nodes_as_voronoi=False : bool If True, node data is visualized as colors of faces of a voronoi diagram based on node coordinates rather than color of circles at node coordinates. node_alpha=1 : float Transparency of nodes. node_quantity_cmap=None : colormap or None Colormap for node_data. node_quantity_clim=None : (float, float) or None Color limits for node_data. node_quantity_alpha=1 : float Transparency of colors used to represent node_data. node_quantity_logarithmic_colors=False : bool If True, node_data color-scale is logarithmic. node_label="" : str Label given to the node colormap. face_quantity_cmap=None : colormap or None Colormap for face_data. face_quantity_clim=None : (float, float) or None Color limits for face_data. face_quantity_alpha=1 : float Transparency of colors used to represent face_data. face_quantity_logarithmic_colors=False : bool If True, face_data color-scale is logarithmic. face_label="" : str Label given to the face colormap. figsize=None : (float, float) or None Size of figure in inches. title="" : str Title given to figure. fig=None : matlplotlib figure or None figure where the axis is embedded. If None, a new figure is created. """ def __init__(self, circuit: Circuit, node_data=None, arrow_data=None, face_data=None, vortex_data=None, vortex_diameter=0.25, vortex_color=(0, 0, 0), anti_vortex_color=(0.8, 0.1, 0.2), vortex_alpha=1, vortex_label="", show_grid=True, grid_width=1, grid_color=(0.4, 0.5, 0.6), grid_alpha=0.5, show_colorbar=True, show_legend=True, legend_width_fraction=0.2, show_axes=True, axis_position=(0.1, 0.1, 0.85, 0.85), arrow_width=0.005, arrow_scale=1, arrow_headwidth=3, arrow_headlength=3.5, arrow_headaxislength=3, arrow_minshaft=1, arrow_minlength=1, arrow_color=(0.15, 0.3, 0.8), arrow_alpha=1, arrow_label="", show_nodes=True, node_diameter=0.25, node_face_color=(1,1,1), node_edge_color=(0, 0, 0), nodes_as_voronoi=False, node_alpha=1, node_quantity_cmap=None, node_quantity_clim=None, node_quantity_alpha=1, node_quantity_logarithmic_colors=False, node_label="", face_quantity_cmap=None, face_quantity_clim=None, face_quantity_alpha=1, face_quantity_logarithmic_colors=False, face_label="", figsize=None, title="", _vortex_range=None, fig=None): self.circuit = circuit self.node_data = node_data self.arrow_data = arrow_data self.face_data = face_data self.vortex_data = np.array(vortex_data, dtype=int) if vortex_data is not None else vortex_data self.show_vortices = self.vortex_data is not None self.vortex_diameter = vortex_diameter self.vortex_color = vortex_color self.anti_vortex_color = anti_vortex_color self.vortex_alpha = vortex_alpha self.vortex_label = vortex_label self._assign_vortex_range(_vortex_range) self.show_grid = show_grid self.grid_width = grid_width self.grid_color = grid_color self.grid_alpha = grid_alpha self.show_colorbar = show_colorbar self.show_legend = show_legend self.legend_width_fraction=legend_width_fraction self.show_axes = show_axes self.axis_position = axis_position self.show_arrows = self.arrow_data is not None self.arrow_width = arrow_width self._assign_arrow_scale(arrow_scale) self.arrow_headwidth = arrow_headwidth self.arrow_headlength = arrow_headlength self.arrow_headaxislength = arrow_headaxislength self.arrow_minshaft = arrow_minshaft self.arrow_minlength = arrow_minlength self.arrow_color = arrow_color self.arrow_alpha = arrow_alpha self.arrow_label = arrow_label self.show_nodes = show_nodes self.node_diameter = node_diameter self.node_face_color = node_face_color self.node_edge_color = node_edge_color self.node_alpha = node_alpha self.show_node_quantity = (self.node_data is not None) and show_nodes self.node_quantity_cmap = node_quantity_cmap if self.node_quantity_cmap is None: self.node_quantity_cmap = "twilight" self.node_quantity_clim = node_quantity_clim self.node_quantity_alpha = node_quantity_alpha self.node_quantity_logarithmic_colors = node_quantity_logarithmic_colors self.node_label = node_label self.nodes_as_voronoi = nodes_as_voronoi self.show_face_quantity = self.face_data is not None self.face_quantity_cmap = face_quantity_cmap if self.face_quantity_cmap is None: self.face_quantity_cmap = "pink" self.face_quantity_clim = face_quantity_clim self.face_quantity_alpha = face_quantity_alpha self.face_quantity_logarithmic_colors = face_quantity_logarithmic_colors self.face_label = face_label self.figsize = figsize if figsize is not None else [6.4, 5] self.colorbar1 = None self.cb1_label = None self.colorbar2 = None self.cb2_label = None self.title = title self.fig = fig self.ax = None self.ax_legend = None self.ax_cb1 = None self.ax_cb2 = None self.grid_handle = None self.node_handle = None self.face_handle = None self.arrow_handle = None self.vortex_handles = [] def _prepare_legend(self, left, bottom, width, height): self.ax_legend = self.fig.add_axes([left, bottom, width, height]) self.legend_stack = [] self.legend_stack += [0.75] if self.show_arrows else [0.93] self.legend_stack += [0.75] if self.show_arrows else [0.93] s = self.show_vortices and len(self._vortex_range) > 0 self.legend_stack += [self._vortex_legend_get_ymin(self.legend_stack[-1])[0]] if s else [self.legend_stack[-1]] s = max(0, self.legend_stack[-1] - 0.5) if self.show_node_quantity and self.show_face_quantity: self.ax_cb1 = self.ax_legend.inset_axes([0.01, 0.02 + s, 0.18, min(self.legend_stack[-1] - 0.1, 0.4)]) else: # at most one colorbar self.ax_cb1 = self.ax_legend.inset_axes([0.3, 0.02 + s, 0.18, min(self.legend_stack[-1] - 0.1, 0.4)]) self.ax_cb2 = self.ax_legend.inset_axes([0.64, 0.02 + s, 0.18, min(self.legend_stack[-1] - 0.1, 0.4)])
[docs] def make(self): """ Make circuit plot. Returns ------- fig : figure handle ax : axis handle """ if self.fig is None: self.fig = plt.figure(figsize=self.figsize) tot_width = self.axis_position[2] l_width = self.legend_width_fraction * tot_width width = tot_width - l_width left = self.axis_position[0] bottom, height = self.axis_position[1], self.axis_position[3] spacing = 0.005 if self.show_legend: self.ax = self.fig.add_axes([left, bottom, width, height]) self._prepare_legend(left + width + spacing, bottom, l_width, height) else: self.ax = self.fig.add_axes(self.axis_position) self.ax.set_title(self.title) self._set_axes() if not self.show_axes: self.ax.set_axis_off() # plot data if self.show_grid: self._plot_grid() if self.show_face_quantity: self._plot_faces() if self.show_nodes: self._plot_nodes() if self.show_arrows: self._plot_arrows() if self.show_vortices: self._plot_vortices() if self.show_legend: if self.colorbar1 is None: self.ax_cb1.set_axis_off() if self.colorbar2 is None: self.ax_cb2.set_axis_off() self._make_legend() xmin, xmax, ymin, ymax = self._get_lims() self.ax.set_xlim(xmin, xmax) self.ax.set_ylim(ymin, ymax) # return handles return self.fig, self.ax
[docs] def get_node_data(self): """ Get data visualized at nodes. """ return self.node_data
[docs] def get_arrow_data(self): """ Get data visualized with arrows. """ return self.arrow_data
[docs] def get_junction_data(self): """ Get data visualized with junctions. """ return self.arrow_data
[docs] def get_face_data(self): """ Get data visualized at faces with colors. """ return self.face_data
[docs] def get_vortex_data(self): """ Get integer data visualized at faces with symbols. """ return self.vortex_data
[docs] def get_figure_handle(self): """ Returns figure handle. """ return self.fig
[docs] def get_axes_handle(self): """ Returns main axes containing the visualization of the circuit. """ return self.fig
[docs] def get_legend_axis_handle(self): """ Get axis-handle containing the legend (with arrow-scale, vortex symbol legend and colormaps). """ return self.ax_legend
[docs] def get_colorbar1_handle(self): """ Get handle of first (left) colorbar. Representing node-or face data colors. """ return self.colorbar1
[docs] def get_colorbar2_handle(self): """ Get handle of second (right) colorbar. """ return self.colorbar2
[docs] def get_node_data_handle(self): """ Get handle containing visualization of node_data. """ return self.node_handle
[docs] def get_face_data_handle(self): """ Get handle containing visualization of face_data. """ return self.face_handle
[docs] def get_vortex_data_handles(self): """ Get handles containing vortex symbols. """ return self.vortex_handles
[docs] def get_arrow_data_handles(self): """ Get handle containing visualization of arrow_data. """ return self.arrow_handle
[docs] def get_grid_handle(self): """ Get handle containing grid. """ return self.grid_handle
# def _return_figure_handles(self): # handles = [self.fig, self.ax, self.colorbar1, self.colorbar2] # return [h for h in handles if h is not None] def _assign_arrow_scale(self, arrow_scale): self.arrow_scale = arrow_scale if self.show_arrows and self.arrow_scale is None: self.arrow_scale = np.max(np.abs(self.arrow_data)) self.arrow_scale = 1 if np.isclose(self.arrow_scale, 0) else self.arrow_scale def _assign_vortex_range(self, vortex_range): self._vortex_range = vortex_range if vortex_range is None and self.show_vortices: self._vortex_range = np.unique(self.vortex_data) if self._vortex_range is not None: self._vortex_range = self._vortex_range[self._vortex_range != 0] def _get_lims(self): x, y = self.circuit.get_node_coordinates() xmin, xmax, ymin, ymax = np.min(x), np.max(x), np.min(y), np.max(y) dx, dy = xmax - xmin, ymax - ymin D = self.node_diameter * 0.5 return xmin - 0.05 * dx - D, xmax + 0.05 * dx + D, ymin - 0.05 * dy - D, ymax + 0.05 * dy + D def _set_axes(self, ): xmin, xmax, ymin, ymax = self._get_lims() self.ax.set_xlim(xmin, xmax) self.ax.set_ylim(ymin, ymax) self.time_label = self.ax.annotate("", (xmin + 0.99 * (xmax - xmin), ymin + 0.98 * (ymax - ymin)), color=[1, 0.5, 0.2], ha='right', va='center') x0, y0, width, height = self.ax.get_position().bounds a_width = width * self.figsize[0] a_height = height * self.figsize[1] if a_width / (xmax - xmin) > a_height / (ymax - ymin): new_width = a_height * (xmax - xmin) / (ymax - ymin) / self.figsize[0] x0 = x0 + (width - new_width) / 2 width = new_width else: new_height = a_width * (ymax - ymin) / (xmax - xmin) / self.figsize[1] y0 = y0 + (height - new_height) / 2 height = new_height self.ax.set_position([x0, y0, width, height]) def _marker_scale_factor(self): return self._scale_factor(self.ax, True) * 72 def _scale_factor(self, ax, on_x): x0, y0, width, height = ax.get_position().bounds if on_x: xlim = ax.get_xlim() return (width * self.figsize[0]) / (xlim[1] - xlim[0]) else: ylim = ax.get_ylim() return (height * self.figsize[1]) / (ylim[1] - ylim[0]) def _plot_grid(self): x1, y1, x2, y2 = self.circuit.get_juncion_coordinates() lines = [((x1[i], y1[i]), (x2[i], y2[i])) for i in range(len(x1))] lc = LineCollection(lines, colors=self.grid_color, alpha=self.grid_alpha,linewidths=self.grid_width, zorder=0) self.grid_handle = self.ax.add_collection(lc) @staticmethod def _voronoi(ax, x, y, data, cnorm, cmap, alpha): points = np.stack((x.flatten(), y.flatten()), axis=0).T xm, xp = ax.get_xlim() ym, yp = ax.get_ylim() points = np.append(points, [[xm - 1, ym - 1], [xm - 1, yp + 1], [xp + 1, yp + 1], [xp + 1, ym - 1]], axis=0) vor = Voronoi(points) verts = [vor.vertices[region, :] for region in vor.regions] verts = [verts[i] for i in vor.point_region[:len(x)]] coll = PolyCollection(verts, array=data, edgecolors='none', cmap=cmap, norm=cnorm, alpha=alpha, zorder=-1) return ax.add_collection(coll) def _cnorm(self, clim, data, logarithmic): if clim is None: clim = (np.min(data), np.max(data)) return clim, Normalize(*clim) if not logarithmic else LogNorm(*clim) def _assign_colorbar(self, cmap, clim, label, cb_nr): if cb_nr == 0: self.colorbar1 = matplotlib.colorbar.Colorbar(ax=self.ax_cb1, mappable=cmap) self.cb1_label = label if np.allclose(clim, [-np.pi, np.pi]): self._set_ax_pi_ticks(self.colorbar1) else: self.ax_cb1.set_ylim(clim) else: self.colorbar2 = matplotlib.colorbar.Colorbar(ax=self.ax_cb2, mappable=cmap) self.cb2_label = label if np.allclose(clim, [-np.pi, np.pi]): self._set_ax_pi_ticks(self.colorbar2) else: self.ax_cb2.set_ylim(clim) def _plot_nodes(self): x, y = self.circuit.get_node_coordinates() marker_size = self.node_diameter * self._marker_scale_factor() if not self.show_node_quantity: node_handle = self.ax.plot(x, y, markeredgecolor=self.node_edge_color, markerfacecolor=self.node_face_color, linestyle="None", markersize=marker_size, marker="o", alpha=self.node_alpha, zorder=2) self.node_handle = node_handle[0] else: clim, cnorm = self._cnorm(self.node_quantity_clim, self.node_data, self.node_quantity_logarithmic_colors) if self.nodes_as_voronoi: self.node_handle = self._voronoi(self.ax, x, y, self.node_data, cnorm, self.node_quantity_cmap, self.node_quantity_alpha) else: self.node_handle = self.ax.scatter(x.flatten(), y.flatten(), s=marker_size**2, c=self.node_data, cmap=self.node_quantity_cmap, edgecolors=None, alpha=self.node_quantity_alpha, norm=cnorm) if self.show_colorbar and self.show_legend: cmap = cm.ScalarMappable(norm=cnorm, cmap=self.node_quantity_cmap) if self.colorbar1 is None: self._assign_colorbar(cmap, clim, self.node_label, cb_nr=0) else: self._assign_colorbar(cmap, clim, self.node_label, cb_nr=1) def _set_ax_pi_ticks(self, cb): cb.set_ticks([-np.pi, -0.5 * np.pi, 0, 0.5*np.pi, np.pi]) cb.set_ticklabels(["$-\pi$", "$-\pi/2$", "$0$", "$\pi/2$", "$\pi$"]) def _plot_arrows(self): I = self.arrow_data * self.arrow_scale x1, y1, x2, y2 = self.circuit.get_juncion_coordinates() xq = x1 + 0.5 * (1 - I) * (x2 - x1) yq = y1 + 0.5 * (1 - I) * (y2 - y1) dxq, dyq = I * (x2 - x1), I * (y2 - y1) self.arrow_handle = self.ax.quiver(xq, yq, dxq, dyq, edgecolor=self.arrow_color, facecolor=self.arrow_color, angles='xy', scale=1, scale_units='xy', width=self.arrow_width, headwidth=self.arrow_headwidth, headlength=self.arrow_headlength, headaxislength=self.arrow_headaxislength, minshaft=self.arrow_minshaft, minlength=self.arrow_minlength, alpha=self.arrow_alpha, zorder=3) def _plot_faces(self): face_nodes = self.circuit.get_faces() x, y = self.circuit.get_node_coordinates() verts = [np.stack((x[n], y[n]), axis=-1) for n in face_nodes] clim = self.face_quantity_clim if clim is None: clim = (np.min(self.face_data), np.max(self.face_data)) cnorm = Normalize(*clim) if not self.face_quantity_logarithmic_colors else LogNorm(*clim) coll = PolyCollection(verts, array=self.face_data, edgecolors='none', cmap=self.face_quantity_cmap, norm=cnorm, alpha=self.face_quantity_alpha, zorder=-1) self.face_handle = self.ax.add_collection(coll) if self.show_colorbar and self.show_legend: cmap = cm.ScalarMappable(norm=cnorm, cmap=self.face_quantity_cmap) self._assign_colorbar(cmap, clim, self.face_label, cb_nr=0) def _plot_vortices(self): n = self.vortex_data marker_size = self.vortex_diameter * self._marker_scale_factor() xc, yc = self.circuit.get_face_centroids() ns = self._vortex_range vort_handles = [] for ni in ns: n_mask = n == ni xni, yni = xc[n_mask], yc[n_mask] p = self._draw_vortex(self.ax, xni, yni, ni, self.vortex_color, self.anti_vortex_color, marker_size, self.vortex_alpha) vort_handles += p self.vortex_handles = vort_handles def _update_nodes(self, new_node_data): if new_node_data is not None and self.show_node_quantity: self.node_handle.set_array(new_node_data) def _update_arrows(self, new_arrow_data): if new_arrow_data is not None and self.show_arrows: I = new_arrow_data * self.arrow_scale x1, y1, x2, y2 = self.circuit.get_juncion_coordinates() xq, yq = x1 + 0.5 * (1 - I) * (x2 - x1), y1 + 0.5 * (1 - I) * (y2 - y1) U, V = I * (x2 - x1), I * (y2 - y1) self.arrow_handle.set_UVC(U, V) self.arrow_handle.X = xq self.arrow_handle.Y = yq self.arrow_handle.XY = np.concatenate((xq[:, None], yq[:, None]), axis=1) self.arrow_handle._offsets = self.arrow_handle.XY def _update_faces(self, new_face_data): if new_face_data is not None and self.show_face_quantity: self.face_handle.set_array(new_face_data) def _update_vortices(self, new_vortex_data): if new_vortex_data is not None and self.show_vortices: n = new_vortex_data xc, yc = self.circuit.get_face_centroids() ns = self._vortex_range index = 0 for i, ni in enumerate(ns): na = np.abs(ni) for handle in self.vortex_handles[index:(index + 2 * na)]: handle.set_xdata(xc[n == ni]) handle.set_ydata(yc[n == ni]) index += 2 * na - 1 def _make_legend(self): self.ax_legend.set_xlim([0, 1]) self.ax_legend.set_ylim([0, 1]) self.ax_legend.set_axis_off() # arrow legend self.ax_legend.text(0.5, 0.975, "legend", ha="center", va="center", fontsize=13) if self.show_arrows: self._arrow_legend(0.75, 0.93) # vortex legend if self.show_vortices and len(self._vortex_range) > 0: self._vortex_legend(self.legend_stack[0]) # node/face legend if self.cb1_label is not None: if self.cb2_label is not None: self.ax_legend.text(0.11, self.legend_stack[-1] - 0.05, self.cb1_label, ha="center", va="center") else: self.ax_legend.text(0.4, self.legend_stack[-1] - 0.05, self.cb1_label, ha="center", va="center") if self.cb2_label is not None: self.ax_legend.text(0.75, self.legend_stack[-1] - 0.05, self.cb2_label, ha="center", va="center") @staticmethod def _nearest_anchor(x): b = np.log(x)/np.log(10) decade = np.floor(b) B = b - decade s = np.array([0, np.log(2)/np.log(10), np.log(5)/np.log(10), 1]) sub = np.argmin(np.abs(B - s)) if sub == 0: return 10 ** decade if sub == 1: return 2 * 10 ** decade if sub == 2: return 5 * 10 ** decade if sub == 3: return 10 ** (decade + 1) def _legend_arrow_scale(self): _, _, width, _ = self.ax.get_position().bounds _, _, width_L, _ = self.ax_legend.get_position().bounds xlim = self.ax.get_xlim() xlim_L = self.ax_legend.get_xlim() v = np.median(self.circuit._junction_lengths()) dx = xlim[1] - xlim[0] dx_L = xlim_L[1] - xlim_L[0] return v * (width / dx) / (width_L / dx_L), v / self.arrow_scale def _arrow_legend(self, y_min, y_max): L, label = self._legend_arrow_scale() label *= (0.51 / L) L = 0.51 new_label = self._nearest_anchor(label) L *= new_label / label ax =self.ax_legend y = 0.6 * y_min + 0.4 * y_max xc = 0.58 hL = L/2 ax.text(0.12, y, self.arrow_label, ha="center", va="center") ax.quiver([xc-hL], [y], [L], [0], edgecolor=self.arrow_color, facecolor=self.arrow_color, scale_units="xy", angles="xy", scale=1, width=0.05, headlength=3, headaxislength=2.2) dy = 0.01 Y = 0.4 * y_min + 0.6 * y_max ax.plot([xc-hL, xc-hL, xc-hL, xc+hL, xc+hL, xc+hL], [Y-dy, Y+dy, Y, Y, Y+dy, Y-dy], color=[0, 0, 0]) yy = 0.2 * y_min + 0.8 * y_max ax.text(xc, yy, f"{new_label}", ha="center", va="center") def _vortex_legend_get_ymin(self, y_max): n_range= self._vortex_range y_min = -np.inf scale_factor_x = self._scale_factor(self.ax_legend, True) scale_factor_y = self._scale_factor(self.ax_legend, False) d = 0.3 while y_min < 0.25: d *= 0.8 Dy = 1.1 * d * (len(n_range) + 1) / (scale_factor_y / scale_factor_x) y_min = y_max - Dy return y_min, d def _vortex_legend(self, y_max): ax = self.ax_legend n_range= self._vortex_range v_color, av_color = self.vortex_color, self.anti_vortex_color label = self.vortex_label scale_factor_x = self._scale_factor(ax, True) y_min, d = self._vortex_legend_get_ymin(y_max) Y = np.linspace(y_max, y_min, 2 * len(n_range) + 2) y = Y[2:-1:2] ax.text(0.12, Y[1], label, ha="center", va="bottom") ax.text(0.6, Y[1], "symbol", ha="center", va="bottom") for i, n in enumerate(n_range): CircuitPlot._draw_vortex(ax, 0.6, y[i], n, v_color, av_color, scale_factor_x * d * 72, 1) ax.text(0.12, y[i], f"{n}", va="center", ha="center") plt.plot([0.02, 0.99, 0.99, 0.02, 0.02], [y_min, y_min, y_max+0.016, y_max+0.016, y_min], color=[0, 0, 0]) return y_min @staticmethod def _draw_vortex(ax, x, y, n, v_color, av_color, diameter, alpha): color = v_color if n > 0 else av_color na = np.abs(n) handles = [] for k in reversed(range(na)): frac = (2 * k + 1) / (2 * na - 1) p = ax.plot(x, y, markerfacecolor=color, markeredgecolor=color, marker="o", linestyle="", markersize=frac * diameter, alpha=alpha, zorder=4) handles += p if k > 0: frac = (2 * k) / (2 * na - 1) p = ax.plot(x, y, markerfacecolor=[1, 1, 1], markeredgecolor=[1, 1, 1], marker="o", linestyle="", markersize=frac * diameter, alpha=alpha, zorder=4) handles += p return handles
[docs]class CircuitMovie(CircuitPlot): """Animation of circuit where the quantities evolve over time. Allows one to show node quantities, arrow quantities, face quantities and vortices. - Node quantities are shown as colors on the nodes. - Arrow quantities are displayed at the junctions and the length of the arrows is proportional to the quantity value. - Face quantities are shown as colors on the faces. - Vortices are displayed by symbols (concentric rings, vorticity equals nr of rings, color shows sign). Base class used by :py:attr:`circuit_visualize.TimeEvolutionMovie`, where one can specify what physical quantities to display in the circuit. Parameters ---------- circuit : :py:attr:`josephson_circuit.Circuit` Object representing circuit. animate_interval : scalar Frame delay in ms. node_data=None : (Nn, time_points) array or (Nn,) array or None Data associated with nodes in circuit. Can be visualized as colors. If None, node data is not visualized. Time independent if shape (Nn,). arrow_data=None : (Nj, time_points) array or (Nj,) array or None Data associated with junctions in circuit. Can be visualized as arrows. where the length corresponds with the magnitude of the data. If None, arrow data is not visualized. Time independent if shape (Nj,). face_data=None : (Nf, time_points) array or (Nf,) array or None Data associated with faces in circuit. Can be visualized as colors. If None, face data is not visualized. Time independent if shape (Nf,). vortex_data=None : (Nf, time_points) int array or (Nf,) int array or None Integer data associated with faces in circuit. Can be visualized as circular symbols. The value of the data equals the number of concentric rings of the symbol. The color shows if it is + or -. If None, vortex data is not visualized. Time independent if shape (Nf,). vortex_diameter=0.25 : float Diameter of vortex symbols. vortex_color=(0, 0, 0) : color Color of vortex symbols. anti_vortex_color=(0.8, 0.1, 0.2) : color Color of anti-vortex symbols, whose data is negative. vortex_alpha=1 : float Transparency of vortex symbols. vortex_label="" : str Label given to vortex data in legend. show_grid=True : bool Display a grid at the edges of the graph. grid_width=1 : float Width of lines of grid. grid_color=(0.4, 0.5, 0.6) : color Color of grid. grid_alpha=0.5 : float Transparency of grid. show_colorbar=True : bool Show colorbar mapping face and/or node data to colors. show_legend=True : bool Show legend which includes colormaps, explanation of vortex sybols and an arrow scale. legend_width_fraction=0.2 : float Fraction of the width of the axes (as specified by axis_position) dedicated to the legend; if it is shown. show_axes=True : bool If True, shows axes with the coordinates of the circuit. axis_position=(0.1, 0.1, 0.85, 0.85) : array_like Position of axis in figure (x0, y0, dx, dy, between 0 and 1) arrow_width=0.005 : float Width of arrows. arrow_scale=1 : float Scale-factor for arrows. (length of arrow = arrow_scale * arrow_data) arrow_headwidth=3 : float Width of head of arrows. (see matplotlib.quiver) arrow_headlength=3.5 : float Length of head of arrows. (see matplotlib.quiver) arrow_headaxislength=3 : float Arrow property. (see matplotlib.quiver) arrow_minshaft=1 : float Arrow property. (see matplotlib.quiver) arrow_minlength=1 : float Arrow property. (see matplotlib.quiver) arrow_color=(0.15, 0.3, 0.8) : color Color of arrows. arrow_alpha=1 : float Transparency of arrows. arrow_label="" : str Label given to arrow data in legend. show_nodes=True : bool If True, nodes are displayed as circles. node_diameter=0.25 : float Diameter of nodes. node_face_color=(1,1,1) : color Color of faces of nodes. Only used if there is no node data. node_edge_color=(0, 0, 0) : color Color of edge of nodes. Only used if there is no node data. nodes_as_voronoi=False : bool If True, node data is visualized as colors of faces of a voronoi diagram based on node coordinates rather than color of circles at node coordinates. node_alpha=1 : float Transparency of nodes. node_quantity_cmap=None : colormap or None Colormap for node_data. node_quantity_clim=None : (float, float) or None Color limits for node_data. node_quantity_alpha=1 : float Transparency of colors used to represent node_data. node_quantity_logarithmic_colors=False : bool If True, node_data color-scale is logarithmic. node_label="" : str Label given to the node colormap. face_quantity_cmap=None : colormap or None Volormap for face_data face_quantity_clim=None : (float, float) or None Color limits for face_data. face_quantity_alpha=1 : float Transparency of colors used to represent face_data. face_quantity_logarithmic_colors=False : bool If True, face_data color-scale is logarithmic. face_label="" : str Label given to the face colormap. figsize=None : (float, float) or None Size of figure in inches. title="" : str Title given to figure. """ def __init__(self, circuit: Circuit, animate_interval=10, node_data=None, arrow_data=None, face_data=None, vortex_data=None, vortex_diameter=0.25, vortex_color=(0, 0, 0), anti_vortex_color=(0.8, 0.1, 0.2), vortex_alpha=1, vortex_label="", _vortex_range=None, show_grid=True, grid_width=1, grid_color=(0.4, 0.5, 0.6), grid_alpha=0.5, show_colorbar=True, show_legend=True, legend_width_fraction=0.2, show_axes=True, axis_position=(0.1, 0.1, 0.85, 0.85), arrow_width=0.005, arrow_scale=1, arrow_headwidth=3, arrow_headlength=3.5, arrow_headaxislength=3, arrow_minshaft=1, arrow_minlength=1, arrow_color=(0.15, 0.3, 0.8), arrow_alpha=1, arrow_label="", show_nodes=True, node_diameter=0.25, node_face_color=(1,1,1), node_edge_color=(0, 0, 0), nodes_as_voronoi=False, node_alpha=1, node_quantity_cmap=None, node_quantity_clim=None, node_quantity_alpha=1, node_quantity_logarithmic_colors=False, node_label="", face_quantity_cmap=None, face_quantity_clim=None, face_quantity_alpha=1, face_quantity_logarithmic_colors=False, face_label="", figsize=None, title="", fig=None): def handle_data(data, N): if data is None: return None, 1 data = np.array(data) if data.ndim == 1: data = data[:, None] if data.ndim != 2 or data.shape[0] != N: raise ValueError(f"data must None, of shape ({N},), ({N}, 1) or ({N}, Nt)") Nt = data.shape[1] return data, Nt self.all_node_data, Ntn = handle_data(node_data, circuit.node_count()) self.all_arrow_data, Nta = handle_data(arrow_data, circuit.junction_count()) self.all_face_data, Ntf = handle_data(face_data, circuit.face_count()) self.all_vortex_data, Ntv = handle_data(vortex_data, circuit.face_count()) Nts = np.array([Ntn, Nta, Ntf, Ntv], dtype=int) self.Nt = np.max(Nts) if not np.all((Nts == 1) | (Nts == self.Nt)): raise ValueError("Not all data has same number of timesteps") if self.all_node_data is not None: if node_quantity_clim is None: node_quantity_clim = (np.min(self.all_node_data), np.max(self.all_node_data)) if self.all_face_data is not None: if face_quantity_clim is None: face_quantity_clim = (np.min(self.all_face_data), np.max(self.all_face_data)) super().__init__(circuit, node_data=self.all_node_data[:, 0] if self.all_node_data is not None else None, arrow_data=self.all_arrow_data[:, 0] if self.all_arrow_data is not None else None, face_data=self.all_face_data[:, 0] if self.all_face_data is not None else None, vortex_data=self.all_vortex_data[:, 0] if self.all_vortex_data is not None else None, vortex_label=vortex_label, node_label=node_label, arrow_label=arrow_label, face_label=face_label, vortex_diameter=vortex_diameter, vortex_color=vortex_color, anti_vortex_color=anti_vortex_color, vortex_alpha=vortex_alpha, show_grid=show_grid, grid_width=grid_width, grid_color=grid_color, grid_alpha=grid_alpha, show_colorbar=show_colorbar, show_legend=show_legend, legend_width_fraction=legend_width_fraction, show_axes=show_axes, axis_position=axis_position, arrow_width=arrow_width, arrow_scale=arrow_scale, arrow_headwidth=arrow_headwidth, arrow_headlength=arrow_headlength, arrow_headaxislength=arrow_headaxislength, arrow_minshaft=arrow_minshaft, arrow_minlength=arrow_minlength, arrow_color=arrow_color, arrow_alpha=arrow_alpha, show_nodes=show_nodes, node_diameter=node_diameter, node_face_color=node_face_color, node_edge_color=node_edge_color, node_alpha=node_alpha, nodes_as_voronoi=nodes_as_voronoi, node_quantity_cmap=node_quantity_cmap, node_quantity_clim=node_quantity_clim, node_quantity_alpha=node_quantity_alpha, node_quantity_logarithmic_colors=node_quantity_logarithmic_colors, face_quantity_cmap=face_quantity_cmap, face_quantity_clim=face_quantity_clim, face_quantity_alpha=face_quantity_alpha, face_quantity_logarithmic_colors=face_quantity_logarithmic_colors, figsize=figsize, title=title, fig=fig) self.animate_interval = animate_interval def _init(self): handles = [self.face_handle, self.node_handle, self.arrow_handle] + self.vortex_handles return [h for h in handles if h is not None] def _animate(self, i): if self.show_face_quantity: if self.all_face_data.shape[1] > 1: self._update_faces(self.all_face_data[:, i]) if self.show_node_quantity: if self.all_node_data.shape[1] > 1: self._update_nodes(self.all_node_data[:, i]) if self.show_arrows: if self.all_arrow_data.shape[1] > 1: self._update_arrows(self.all_arrow_data[:, i]) if self.show_vortices: if self.all_vortex_data.shape[1] > 1: self._update_vortices(self.all_vortex_data[:, i]) handles = [self.node_handle, self.arrow_handle, self.face_handle] + self.vortex_handles return [h for h in handles if h is not None]
[docs] def make(self): """ Make circuit plot. Returns ------- fig : Figure handle. ax : Axis handle. """ super().make() self.ani = animation.FuncAnimation(self.fig, self._animate, np.arange(self.Nt, dtype=int), init_func=self._init, interval=self.animate_interval, blit=True) return self.fig, self.ax
[docs] def get_animation_handle(self): """ Return FuncAnimation handle. """ return self.ani
def _assign_arrow_scale(self, arrow_scale): self.arrow_scale = arrow_scale if self.show_arrows and self.arrow_scale is None: self.arrow_scale = np.max(np.abs(self.all_arrow_data)) self.arrow_scale = 1 if np.isclose(self.arrow_scale, 0) else self.arrow_scale def _assign_vortex_range(self, vortex_range): self._vortex_range = vortex_range if vortex_range is None and self.show_vortices: self._vortex_range = np.unique(self.all_vortex_data) if self._vortex_range is not None: self._vortex_range = self._vortex_range[self._vortex_range != 0]
[docs]class ConfigPlot(CircuitPlot): """ Visualize static configuration on a circuit. Allows one to show node quantities, junction quantities, face quantities and vortices. - Node quantities are shown as colors on the nodes. - Junction quantities are displayed at the junctions as arrows, where the length of the arrows is proportional to the quantity value. - Face quantities are shown as colors on the faces. - Vortices are displayed by symbols (concentric rings, vorticity equals nr of rings, color shows sign). Parameters ---------- config : :py:attr:`static_problem.StaticConfiguration` Static configuration to plot. node_quantity=None : str or None What physical quantity of config to visualize at nodes. Options: * "" or None : no quantity displayed * "phi" : gauge dependent phases * "Is_node" : current sourced at nodes junction_quantity="I" : str or None What physical quantity of config to visualize on junctions. Options: * "" or None : no quantity displayed * "theta" : gauge invariant phase difference * "I" : current * "Is" : junction current sources * "EJ" : josephson energy * "EM" : magnetic energy * "Etot" : total energy face_quantity=None : str or None What physical quantity of config to visualize at faces. Options: * "" or None : no quantity displayed * "flux" : magnetic flux through face * "J" : cycle-current * "n" : vorticity vortex_quantity="n" : str or None What face-integer physical quantity of config to visualize with vortex symbols. Options: * "" or None : no quantity displayed * "n" : vortices vortex_diameter=0.25 : float Diameter of vortex symbols. vortex_color=(0, 0, 0) : color Color of vortex symbols. anti_vortex_color=(0.8, 0.1, 0.2) : color Color of anti-vortex symbols, whose data is negative. vortex_alpha=1 : float Transparency of vortex symbols. manual_vortex_label=None : str or None Label given to vortices in the legend. show_grid=True : bool Display a grid at the edges of the graph. grid_width=1 : float Width of lines of grid. grid_color=(0.4, 0.5, 0.6) : color Color of grid. grid_alpha=0.5 : float Transparency of grid. show_colorbar=True : bool Show colorbar mapping face and/or node data to colors. show_legend=True : bool Show legend which includes colormaps, explanation of vortex sybols and an arrow scale. legend_width_fraction=0.2 : float Fraction of the width of the axes (as specified by axis_position) dedicated to the legend; if it is shown. show_axes=True : bool If True, shows axes with the coordinates of the circuit. axis_position=(0.1, 0.1, 0.85, 0.85) : array_like Position of axis in figure (x0, y0, dx, dy, between 0 and 1) arrow_width=0.005 : float Width of arrows. arrow_scale=1 : float Scale-factor for arrows. (length of arrow = arrow_scale * arrow_data) arrow_headwidth=3 : float Width of head of arrows. (see matplotlib.quiver) arrow_headlength=3.5 : float Length of head of arrows. (see matplotlib.quiver) arrow_headaxislength=3 : float Arrow property. (see matplotlib.quiver) arrow_minshaft=1 : float Arrow property. (see matplotlib.quiver) arrow_minlength=1 : float Arrow property. (see matplotlib.quiver) arrow_color=(0.15, 0.3, 0.8) : color Color of arrows. arrow_alpha=1 : float Transparency of arrows. manual_arrow_label=None : str or None Label given to arrows in the legend. show_nodes=True : bool If True, nodes are displayed as circles. node_diameter=0.25 : float Diameter of nodes. node_face_color=(1,1,1) : color Color of faces of nodes. Only used if there is no node data. node_edge_color=(0, 0, 0) : color Color of edge of nodes. Only used if there is no node data. nodes_as_voronoi=False : bool If True, node data is visualized as colors of faces of a voronoi diagram based on node coordinates rather than color of circles at node coordinates. node_alpha=1 : float Transparency of nodes. node_quantity_cmap=None : colormap or None Colormap for node_data node_quantity_clim=None : (float, float) or None Color limits for node_data. node_quantity_alpha=1 : float Transparency of colors used to represent node_data. node_quantity_logarithmic_colors=False : bool If True, node_data color-scale is logarithmic. manual_node_label=None : str or None Label given to node data in the legend. face_quantity_cmap=None : colormap or None Colormap for face_data. face_quantity_clim=None : (float, float) or None Color limits for face_data. face_quantity_alpha=1 : float Transparency of colors used to represent face_data. face_quantity_logarithmic_colors=False : bool If True, face_data color-scale is logarithmic. manual_face_label=None : str or None Label given to face data in the legend. figsize=None : (float, float) or None Size of figure in inches. title="" : str Title given to figure. """ def __init__(self, config: StaticConfiguration, node_quantity=None, junction_quantity="I", face_quantity=None, vortex_quantity="n", vortex_diameter=0.25, vortex_color=(0, 0, 0), anti_vortex_color=(0.8, 0.1, 0.2), vortex_alpha=1, _vortex_range=None, manual_vortex_label=None, show_grid=True, grid_width=1, grid_color=(0.4, 0.5, 0.6), grid_alpha=0.5, show_colorbar=True, show_legend=True, legend_width_fraction=0.2, show_axes=True, axis_position=(0.1, 0.1, 0.85, 0.85), arrow_width=0.005, arrow_scale=1, arrow_headwidth=3, arrow_headlength=3.5, arrow_headaxislength=3, arrow_minshaft=1, arrow_minlength=1, arrow_color=(0.15, 0.3, 0.8), arrow_alpha=1, manual_arrow_label=None, show_nodes=True, node_diameter=0.25, node_face_color=(1,1,1), node_edge_color=(0, 0, 0), nodes_as_voronoi=False, node_alpha=1, node_quantity_cmap=None, manual_node_label=None, node_quantity_clim=None, node_quantity_alpha=1, node_quantity_logarithmic_colors=False, face_quantity_cmap=None, face_quantity_clim=None, face_quantity_alpha=1, face_quantity_logarithmic_colors=False, manual_face_label=None, figsize=None, title="", fig=None): self.config = config self.node_quantity = node_quantity if node_quantity is not None else "" self.arrow_quantity = junction_quantity if junction_quantity is not None else "" self.face_quantity = face_quantity if face_quantity is not None else "" self.vortex_quantity = vortex_quantity if vortex_quantity is not None else "" node_data, node_label = self._get_node_quantity() if manual_node_label is not None: node_label = manual_node_label arrow_data, arrow_label = self._get_junction_quantity() if manual_arrow_label is not None: arrow_label = manual_arrow_label face_data, face_label = self._get_face_quantity() if manual_face_label is not None: face_label = manual_face_label vortex_data, vortex_label = self._get_vortex_quantity() if manual_vortex_label is not None: vortex_label = manual_vortex_label if node_label == "phi": if node_quantity_clim is None: node_quantity_clim = (-np.pi, np.pi) else: if node_quantity_cmap is None: node_quantity_cmap = "magma" if arrow_label == "th": if arrow_scale is None: arrow_scale = 1/np.pi super().__init__(config.get_circuit(), node_data = node_data, arrow_data=arrow_data, face_data=face_data, vortex_data=vortex_data, vortex_label=vortex_label, node_label=node_label, arrow_label=arrow_label, face_label=face_label, vortex_diameter=vortex_diameter, vortex_color=vortex_color, anti_vortex_color=anti_vortex_color, vortex_alpha=vortex_alpha, show_grid=show_grid, grid_width=grid_width, grid_color=grid_color, grid_alpha=grid_alpha, show_colorbar=show_colorbar, show_legend=show_legend, legend_width_fraction=legend_width_fraction, show_axes=show_axes, axis_position=axis_position, arrow_width=arrow_width, arrow_scale=arrow_scale, arrow_headwidth=arrow_headwidth, arrow_headlength=arrow_headlength, arrow_headaxislength=arrow_headaxislength, arrow_minshaft=arrow_minshaft, arrow_minlength=arrow_minlength, arrow_color=arrow_color, arrow_alpha=arrow_alpha, show_nodes=show_nodes, node_diameter=node_diameter, node_face_color=node_face_color, node_edge_color=node_edge_color, node_alpha=node_alpha, nodes_as_voronoi=nodes_as_voronoi, node_quantity_cmap=node_quantity_cmap, node_quantity_clim=node_quantity_clim, node_quantity_alpha=node_quantity_alpha, node_quantity_logarithmic_colors=node_quantity_logarithmic_colors, face_quantity_cmap=face_quantity_cmap, face_quantity_clim=face_quantity_clim, face_quantity_alpha=face_quantity_alpha, face_quantity_logarithmic_colors=face_quantity_logarithmic_colors, figsize=figsize, title=title, fig=fig) _node_quantities = { "": -1, "phi": 0, "phase": 0, "phases": 0, "I_s": 1, "Is": 1, "current_sources": 1, } _junction_quantities = { "": -1, "th": 0, "theta": 0, "phase_difference": 0, "gauge_invariant_phase_difference": 0, "I": 1, "current": 1, "Isup": 1, "I_sup": 1, "Isuper": 1, "I_super": 1, "supercurrent": 1, "super_current": 1, "I_s": 2, "Is": 2, "current_sources": 2, "EJ": 3, "josephson_energy": 3, "Ej": 3, "EM": 4, "magnetic_energy": 4, "Em": 4, "Etot": 5, "E_tot": 5, "ETot": 5, "total_energy": 5, "energy": 5, } _face_quantities = { "": -1, "Phi": 0, "flux": 0, "magnetic_flux": 0, "n": 1, "vortices": 1, "vortex_configuration": 1, "face_current": 2, "J": 2, "cycle_current": 2, } _vortex_quantities = { "": -1, "n": 0, "vortices": 0, "vortex_configuration": 0, }
[docs] def get_node_quantity(self): """ Get physical quantity displayed at nodes. (None, "phi" or "Is_node") """ return self.node_label
[docs] def get_junction_quantity(self): """ Get physical quantity displayed at junctions. (None, "th", "I", "Is", "EJ", "EM" or "Etot") """ return self.arrow_label
[docs] def get_face(self): """ Get physical quantity displayed at faces with colors. (None, "flux", " n" or "J") """ return self.face_label
[docs] def get_vortex_quantity(self): """ Get integer physical quantity displayed at faces with symbols. (None or "n") """ return self.vortex_label
def _get_node_quantity(self): if isinstance(self.node_quantity, np.ndarray): return self.node_quantity.flatten(), "custom" quantity = self._node_quantities[self.node_quantity] if quantity == -1: # none return None, None if quantity == 0: # phi out = self.config.get_phase() out = out.copy() out -= np.round(out / (np.pi * 2.0)).astype(out.dtype) * np.pi * 2.0 return out, "phi" if quantity == 1: # Is return self.config.problem.get_node_current_sources(), "Is_node" def _get_junction_quantity(self): if isinstance(self.arrow_quantity, np.ndarray): return self.arrow_quantity.flatten(), "custom" quantity = self._junction_quantities[self.arrow_quantity] if quantity == -1: # none return None, None if quantity == 0: # theta out = self.config.get_theta() out = out.copy() out -= np.round(out / (np.pi * 2.0)).astype(out.dtype) * np.pi * 2.0 return out, "th" if quantity == 1: # I return self.config.get_current(), "I" if quantity == 2: # Is return self.config.problem._Is(), "Is" if quantity == 3: # EJ return self.config.get_josephson_energy(), "EJ" if quantity == 4: # EM return self.config.get_magnetic_energy(), "EM" if quantity == 5: # Etot return self.config.get_energy(), "Etot" def _get_face_quantity(self): if isinstance(self.face_quantity, np.ndarray): return self.face_quantity.flatten(), "custom" quantity = self._face_quantities[self.face_quantity] if quantity == -1: # none return None, None if quantity == 0: # Phi return self.config.get_flux(), "flux" if quantity == 1: # n return self.config.get_vortex_configuration(), "n" if quantity == 2: # J return self.config.get_cycle_current(), "J" def _get_vortex_quantity(self): if isinstance(self.vortex_quantity, np.ndarray): return self.vortex_quantity.flatten().astype(int), "custom" quantity = self._vortex_quantities[self.vortex_quantity] if quantity == -1: # none return None, None if quantity == 0: # n return self.config.get_vortex_configuration(), "n" @staticmethod def _assert_single_configuration(data): if data.ndim >= 2: raise ValueError("must select single configuration")
[docs]class TimeEvolutionMovie(CircuitMovie): """ Visualize time evolution on a circuit as a movie. Allows one to show node quantities, junction quantities, face quantities and vortices. - Node quantities are shown as colors on the nodes. - Junction quantities are displayed at the junctions as arrows, where the length of the arrows is proportional to the quantity value. - Face quantities are shown as colors on the faces. - Vortices are displayed by symbols (concentric rings, vorticity equals nr of rings, color shows sign). Parameters ---------- config : :py:attr:`time_evolution.TimeEvolutionResult` Time evolution to visualize. node_quantity=None : str or None What physical quantity of config to visualize at nodes. Options: * "" or None : no quantity displayed * "phi" : gauge dependent phases * "Is_node" : current sourced at nodes * "U" : Voltage potential junction_quantity="I" : str or None What physical quantity of config to visualize on junctions. Options: * "" or None : no quantity displayed * "theta" : gauge invariant phase difference * "I" : current * "V" : Voltage * "Isuper" : supercurrent * "Is" : junction current sources * "EJ" : josephson energy * "EM" : magnetic energy * "EC" : capacative energy * "Etot" : total energy face_quantity=None : str or None What physical quantity of config to visualize at faces. Options: * "" or None : no quantity displayed * "flux" : magnetic flux through face * "J" : cycle-current * "n" : vorticity vortex_quantity="n" : str or None What face-integer physical quantity of config to visualize with vortex symbols. Options: * "" or None : no quantity displayed * "n" : vortices vortex_diameter=0.25 : float Diameter of vortex symbols. vortex_color=(0, 0, 0) : color Color of vortex symbols. anti_vortex_color=(0.8, 0.1, 0.2) : color Color of anti-vortex symbols, whose data is negative. vortex_alpha=1 : float Transparancy of vortex symbols. manual_vortex_label=None : str or None Label given to vortices in the legend. show_grid=True : bool Display a grid at the edges of the graph. grid_width=1 : float Width of lines of grid. grid_color=(0.4, 0.5, 0.6) : color Color of grid. grid_alpha=0.5 : float Transparency of grid. show_colorbar=True : bool Show colorbar mapping face and/or node data to colors. show_legend=True : bool Show legend which includes colormaps, explanation of vortex sybols and an arrow scale. legend_width_fraction=0.2 : float Fraction of the width of the axes (as specified by axis_position) dedicated to the legend; if it is shown. show_axes=True : bool If True, shows axes with the coordinates of the circuit. axis_position=(0.1, 0.1, 0.85, 0.85) : array_like Position of axis in figure (x0, y0, dx, dy, between 0 and 1) arrow_width=0.005 : float Width of arrows. arrow_scale=1 : float Scale-factor for arrows. (length of arrow = arrow_scale * arrow_data) arrow_headwidth=3 : float Width of head of arrows. (see matplotlib.quiver) arrow_headlength=3.5 : float Length of head of arrows. (see matplotlib.quiver) arrow_headaxislength=3 : float Arrow property. (see matplotlib.quiver) arrow_minshaft=1 : float Arrow property. (see matplotlib.quiver) arrow_minlength=1 : float Arrow property. (see matplotlib.quiver) arrow_color=(0.15, 0.3, 0.8) : color Color of arrows. arrow_alpha=1 : float Transparency of arrows. manual_arrow_label=None : str or None Label given to arrows in the legend. show_nodes=True : bool If True, nodes are displayed as circles node_diameter=0.25 : float Diameter of nodes. node_face_color=(1,1,1) : color Color of faces of nodes. Only used if there is no node data. node_edge_color=(0, 0, 0) : color Color of edge of nodes. Only used if there is no node data. nodes_as_voronoi=False : bool If True, node data is visualized as colors of faces of a voronoi diagram based on node coordinates rather than color of circles at node coordinates. node_alpha=1 : float Transparency of nodes. manual_node_label=None : str or None Label given to node data in the legend. node_quantity_cmap=None : colormap or None Colormap for node_data. node_quantity_clim=None : (float, float) or None Color limits for node_data. node_quantity_alpha=1 : float Transparency of colors used to represent node_data. node_quantity_logarithmic_colors=False : bool If True, node_data color-scale is logarithmic. face_quantity_cmap=None : colormap or None Colormap for face_data. face_quantity_clim=None : (float, float) or None Color limits for face_data. face_quantity_alpha=1 : float Transparency of colors used to represent face_data. face_quantity_logarithmic_colors=False : bool If True, face_data color-scale is logarithmic. manual_face_label=None : str or None Label given to face data in the legend. figsize=None : (float, float) or None Size of figure in inches. title="" : str Title given to figure. """ def __init__(self, config: TimeEvolutionResult, problem_nr=0, time_points=None, node_quantity=None, junction_quantity="I", face_quantity=None, vortex_quantity="n", vortex_diameter=0.25, vortex_color=(0, 0, 0), anti_vortex_color=(0.8, 0.1, 0.2), vortex_alpha=1, _vortex_range=None, manual_vortex_label=None, show_grid=True, grid_width=1, grid_color=(0.4, 0.5, 0.6), grid_alpha=0.5, show_colorbar=True, show_legend=True, legend_width_fraction=0.2, show_axes=True, axis_position=(0.1, 0.1, 0.85, 0.85), arrow_width=0.005, arrow_scale=1, arrow_headwidth=3, arrow_headlength=3.5, arrow_headaxislength=3, arrow_minshaft=1, arrow_minlength=1, arrow_color=(0.15, 0.3, 0.8), arrow_alpha=1, manual_arrow_label=None, show_nodes=True, node_diameter=0.25, node_face_color=(1,1,1), node_edge_color=(0, 0, 0), nodes_as_voronoi=False, node_alpha=1, node_quantity_cmap=None, node_quantity_clim=None, node_quantity_alpha=1, node_quantity_logarithmic_colors=False, manual_node_label=None, face_quantity_cmap=None, face_quantity_clim=None, face_quantity_alpha=1, face_quantity_logarithmic_colors=False, manual_face_label=None, figsize=None, title="", animate_interval=10, fig=None): self.config = config self.problem_nr = problem_nr self.time_points = time_points if self.time_points is None: self.time_points = np.ones(self.config.problem._Nt(), dtype=bool) if not (self.time_points.dtype in (bool, np.bool)): try: self.time_points = np.zeros(self.config.problem._Nt(), dtype=bool) self.time_points[time_points] = True except: raise ValueError("Invalid time_points; must be None, mask, slice or index array") self.time_points &= self.config.problem.store_time_steps def _get_quantity(quantity, getter_func, problem_nr): """ quantity -> "", "I", ndarray """ if quantity == "" or quantity is None: return "none", None elif isinstance(quantity, str): all_data, quantity = getter_func(quantity) else: all_data, quantity = quantity, "custom" return quantity, all_data[:, problem_nr, :] self.node_quantity, node_data = _get_quantity(node_quantity, self._get_node_quantity, problem_nr) self.arrow_quantity, arrow_data = _get_quantity(junction_quantity, self._get_junction_quantity, problem_nr) self.face_quantity, face_data = _get_quantity(face_quantity, self._get_face_quantity, problem_nr) self.vortex_quantity, vortex_data = _get_quantity(vortex_quantity, self._get_vortex_quantity, problem_nr) node_label = manual_node_label if manual_node_label is not None else self.node_quantity arrow_label = manual_arrow_label if manual_arrow_label is not None else self.arrow_quantity face_label = manual_face_label if manual_face_label is not None else self.face_quantity vortex_label = manual_vortex_label if manual_vortex_label is not None else self.vortex_quantity if self.node_quantity == "phi": if node_quantity_clim is None: node_quantity_clim = (-np.pi, np.pi) else: if node_quantity_cmap is None: node_quantity_cmap = "magma" if self.arrow_quantity == "th": if arrow_scale is None: arrow_scale = 1/np.pi super().__init__(config.get_circuit(), node_data=node_data, arrow_data=arrow_data, face_data=face_data, vortex_data=vortex_data, vortex_label=vortex_label, node_label=node_label, arrow_label=arrow_label, face_label=face_label, vortex_diameter=vortex_diameter, vortex_color=vortex_color, anti_vortex_color=anti_vortex_color, vortex_alpha=vortex_alpha, show_grid=show_grid, grid_width=grid_width, grid_color=grid_color, grid_alpha=grid_alpha, show_colorbar=show_colorbar, show_legend=show_legend, legend_width_fraction=legend_width_fraction, show_axes=show_axes, axis_position=axis_position, arrow_width=arrow_width, arrow_scale=arrow_scale, arrow_headwidth=arrow_headwidth, arrow_headlength=arrow_headlength, arrow_headaxislength=arrow_headaxislength, arrow_minshaft=arrow_minshaft, arrow_minlength=arrow_minlength, arrow_color=arrow_color, arrow_alpha=arrow_alpha, show_nodes=show_nodes, node_diameter=node_diameter, node_face_color=node_face_color, node_edge_color=node_edge_color, nodes_as_voronoi=nodes_as_voronoi, node_alpha=node_alpha, node_quantity_cmap=node_quantity_cmap, node_quantity_clim=node_quantity_clim, node_quantity_alpha=node_quantity_alpha, node_quantity_logarithmic_colors=node_quantity_logarithmic_colors, face_quantity_cmap=face_quantity_cmap, face_quantity_clim=face_quantity_clim, face_quantity_alpha=face_quantity_alpha, face_quantity_logarithmic_colors=face_quantity_logarithmic_colors, figsize=figsize, title=title, animate_interval=animate_interval, fig=fig) _node_quantities = { "phi": 0, "phase": 0, "phases": 0, "I_s": 1, "Is": 1, "current_sources": 1, "U": 2, "potential": 2, } _junction_quantities = { "th": 0, "theta": 0, "phase_difference": 0, "gauge_invariant_phase_difference": 0, "I": 1, "current": 1, "V": 2, "voltage": 2, "Isup": 3, "I_sup": 3, "Isuper": 3, "I_super": 3, "supercurrent": 3, "super_current": 3, "I_s": 4, "Is": 4, "current_sources": 4, "EJ": 5, "josephson_energy": 5, "EM": 6, "magnetic_energy": 6, "EC": 7, "capacitive_energy": 7, "capacitance_energy": 7, "Etot": 8, "E_tot": 8, "ETot": 8, "total_energy": 8, "energy": 8, } _face_quantities = { "Phi": 0, "flux": 0, "magnetic_flux": 0, "n": 1, "vortices": 1, "vortex_configuration": 1, "face_current": 2, "J": 2, "cycle_current": 2, } _vortex_quantities = { "n": 0, "vortices": 0, "vortex_configuration": 0, }
[docs] def get_node_quantity(self): """ Get physical quantity displayed at nodes. (None, "phi", "Is_node" or "U") """ return self.node_label
[docs] def get_junction_quantity(self): """ Get physical quantity displayed at junctions. (None, "th", "I", "V", "Isup", "Is", "EJ", "EM", "EC" or "Etot") """ return self.arrow_label
[docs] def get_face(self): """ Get physical quantity displayed at faces with colors. (None, "flux", " n" or "J") """ return self.face_label
[docs] def get_vortex_quantity(self): """ Get physical quantity displayed at faces with symbols. (None or "n") """ return self.vortex_label
def _get_node_quantity(self, node_quantity): """ Get node quantity (either "phi": gauge dependent phases or "U": potential) """ quantity = self._node_quantities[node_quantity] if quantity == 0: # phi out = self.config.get_phase(self.time_points) out = out.copy() out -= np.round(out / (np.pi * 2.0)).astype(out.dtype) * np.pi * 2.0 return out, "phi" if quantity == 1: # Is return self.config.problem.get_node_current_sources(self.time_points), "Is_node" if quantity == 2: # U return self.config.get_potential(self.time_points), "U" def _get_junction_quantity(self, junction_quantity): quantity = self._junction_quantities[junction_quantity] if quantity == 0: # theta out = self.config.get_theta(self.time_points) out = out.copy() out -= np.round(out / (np.pi * 2.0)).astype(out.dtype) * np.pi * 2.0 return out, "th" if quantity == 1: # I return self.config.get_current(self.time_points), "I" if quantity == 2: # V return self.config.get_voltage(self.time_points), "V" if quantity == 3: # supercurrent return self.config.get_supercurrent(self.time_points), "Isup" if quantity == 4: # Is return self.config.problem._Is(self.time_points), "Is" if quantity == 5: # EJ return self.config.get_josephson_energy(self.time_points), "EJ" if quantity == 6: # EM return self.config.get_magnetic_energy(self.time_points), "EM" if quantity == 7: # EC return self.config.get_capacitive_energy(self.time_points), "EC" if quantity == 8: # Etot return self.config.get_energy(self.time_points), "Etot" def _get_face_quantity(self, face_quantity): quantity = self._face_quantities[face_quantity] if quantity == 0: # Phi return self.config.get_flux(self.time_points), "flux" if quantity == 1: # n return self.config.get_vortex_configuration(self.time_points), "n" if quantity == 2: # J return self.config.get_cycle_current(self.time_points), "J" def _get_vortex_quantity(self, vortex_quantity): quantity = self._vortex_quantities[vortex_quantity] if quantity == 0: # n return self.config.get_vortex_configuration(self.time_points), "n"