def get_cached_image(preset_name, image_path):
    """
    retrieve or transparently generate cache for full-sized image
    """
    
    preset = get_preset(preset_name)
    processor = imageprocessor_from_preset(preset)
    cache = ImageCache(preset.output_dir)
    img = cache.get_image(processor, image_path)
    if not img:
        img = CachedImage(processor.process(image_path).save().filename)
    return img
def process_image(preset_name, filename):
    return imageprocessor_from_preset(
            get_preset(preset_name)).process(filename)
Beispiel #3
0
def visualize_fields(config):
    """Plot electric and magnetic fields for given configuration

	Args:
		config: Environment configuration
	"""
    ax3 = config["plane"]["axis"]
    Z = config["plane"]["coordinate"]
    axes = []
    for i in range(3):
        if i == ax3:
            axes.append([Z])
        else:
            x_min, x_max = config["plot-bounds"]["min"][i], config[
                "plot-bounds"]["max"][i]
            x_min -= config["plot-margins"][i]
            x_max += config["plot-margins"][i]
            axis = np.linspace(x_min, x_max,
                               config["resolution"] * int(x_max - x_min))
            axes.append(axis)
    space = np.array(np.meshgrid(*axes))

    axis_names = ["x", "y", "z"]
    ax1, ax2 = {0: (1, 2), 1: (0, 2), 2: (0, 1)}[ax3]
    e_field, b_field = np.zeros_like(space), np.zeros_like(space)

    if "charges" in config:
        charges = np.array(config["charges"])

        def efield_charges(x):
            E = np.zeros_like(x)
            for charge in charges:
                s = (x.T - charge[1:]).T
                E += charge[0] * s / (np.linalg.norm(s, axis=0)**3 + 1e-6)
            return E

        e_field += efield_charges(space)

    list_charge_densities = []
    if "charge-densities" in config:
        densities = config["charge-densities"]
        for density_func in densities:
            if density_func["preset"]:
                rho = presets.get_preset(density_func)
            else:
                rho = construct_function(eval_safety, density_func["func"])
            list_charge_densities.append(rho)

            def integrand(z, y, x, Xz, Xy, Xx, axis):
                X = np.array([Xx, Xy, Xz])
                Y = np.array([x, y, z])
                v = X - Y
                return rho(z, y, x) * v[axis] / (np.linalg.norm(v)**3 + 1e-6)

            def integrand2(y, x, Xz, Xy, Xx, axis, z, axis1, axis2, axis3):
                Y = np.empty(3)
                Y[axis1] = x
                Y[axis2] = y
                Y[axis3] = z
                return integrand(Y[2], Y[1], Y[0], Xz, Xy, Xx, axis)

            def integrand1(x, Xz, Xy, Xx, axis, y, z, axis1, axis2, axis3):
                Y = np.empty(3)
                Y[axis1] = x
                Y[axis2] = y
                Y[axis3] = z
                return integrand(Y[2], Y[1], Y[0], Xz, Xy, Xx, axis)

            def grid_integral(f, *bounds, args=(), dim=3):
                if dim == 3:
                    return np.vectorize(lambda z, y, x: tplquad(
                        f, *bounds, args=(z, y, x, *args))[0]
                                        if rho(z, y, x) == 0 else 0)
                elif dim == 2:
                    return np.vectorize(lambda z, y, x: dblquad(
                        f, *bounds, args=(z, y, x, *args))[0])
                elif dim == 1:
                    return np.vectorize(lambda z, y, x: quad(
                        f, *bounds, args=(z, y, x, *args))[0])
            if density_func["func"] == presets.PRESET_DELTA and \
              density_func["var"] in ["x", "y", "z"]:
                val = density_func["value"]
                axis1, axis2, delAxis = {
                    ("x", 0): (1, 2, 0),
                    ("x", 1): (2, 1, 0),
                    ("x", 2): (1, 2, 0),
                    ("y", 0): (2, 0, 1),
                    ("y", 1): (0, 2, 1),
                    ("y", 2): (0, 2, 1),
                    ("z", 0): (1, 0, 2),
                    ("z", 1): (0, 1, 2),
                    ("z", 2): (0, 1, 2)
                }[(density_func["var"], ax3)]
                ax, ay = axes[axis1][0], axes[axis2][0]
                bx, by = axes[axis1][-1], axes[axis2][-1]
                for axis in range(3):
                    if axis == ax3:
                        continue
                    if delAxis == ax3:
                        # Technically this should never come up
                        # because the field is parallel to the
                        # ignored axis
                        e_field[axis] += grid_integral(integrand2,
                                                       ax,
                                                       bx,
                                                       ay,
                                                       by,
                                                       args=(axis, val, axis1,
                                                             axis2, delAxis),
                                                       dim=2)(space[2],
                                                              space[1],
                                                              space[0])
                    else:
                        e_field[axis] += grid_integral(
                            integrand1,
                            ax,
                            bx,
                            args=(axis, val, Z, axis1, delAxis, axis2),
                            dim=1)(space[2], space[1], space[0])
            else:
                ax, ay, az = axes[0][0], axes[1][0], axes[2][0]
                bx, by, bz = axes[0][-1], axes[1][-1], axes[2][-1]
                if ax == bx:
                    ax, bx = ay, by
                elif ay == by:
                    ay, by = ax, bx
                elif az == bz:
                    az, bz = ax, bx
                for axis in range(3):
                    if axis == ax3:
                        continue
                    e_field[axis] += grid_integral(integrand,
                                                   ax,
                                                   bx,
                                                   ay,
                                                   by,
                                                   az,
                                                   bz,
                                                   args=(axis, ))(space[2],
                                                                  space[1],
                                                                  space[0])

    # Determine overall charge density distribution
    if len(list_charge_densities) > 0:
        overall_charge_density = np.zeros_like(space[0])
    for rho in list_charge_densities:
        overall_charge_density += np.vectorize(rho)(space[2], space[1],
                                                    space[0])

    if ax3 != 2:
        ax1, ax2 = ax2, ax1
        e_field = np.moveaxis(e_field, 2 - ax3, -1)
        b_field = np.moveaxis(b_field, 2 - ax3, -1)
        overall_charge_density = np.moveaxis(overall_charge_density, 1 - ax3,
                                             -1)

    # Generate field plots
    for field_name, field in zip(["e", "b"], [e_field, b_field]):
        config_name = f"{field_name}-field"
        if config[config_name]["plot"]:
            render_name = f"{field_name.upper()}-Field"
            plt.figure(render_name)

            # Plot charges
            if "charges" in config:
                for charge in config["charges"]:
                    xy = (charge[1 + ax1], charge[1 + ax2])
                    plt.plot(*xy,
                             'ro',
                             color=("red" if charge[0] > 0 else "blue"))
                    plt.annotate(f"{charge[0]} C",
                                 xy=xy,
                                 xytext=(10, 5),
                                 ha='right',
                                 textcoords='offset points')

            # Plot charge densities
            if len(list_charge_densities) > 0:
                plt.contourf(axes[ax1],
                             axes[ax2],
                             overall_charge_density.T[0].T,
                             cmap=plt.cm.bwr)

            # Plot field
            fx, fy = field[ax1].T[0].T, field[ax2].T[0].T
            color = 2 * np.log(np.hypot(fx, fy) + 1e-6)
            try:
                plt.streamplot(axes[ax1],
                               axes[ax2],
                               fx,
                               fy,
                               color=color,
                               cmap=plt.get_cmap(config["colormap"]))
            except ValueError as e:
                print(f"Failed to plot {render_name}: {e}")

            # Plot labels
            plt.title(f"{render_name} ({axis_names[ax3]} = {Z})")
            plt.xlabel(axis_names[ax1])
            plt.ylabel(axis_names[ax2])

            # Output
            if output_files[config_name] is not None:
                plt.savefig(output_files[config_name])
            else:
                plt.savefig(f"{config['name']} {render_name}.png")
            if config["show"]:
                plt.show()