diff --git a/README.md b/README.md deleted file mode 100644 index c89d3cb..0000000 --- a/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# pylj - -This is a simple python code to perform simple Lennard-Jonesium 2D simulation. - -The aim of this is to use in a computational/physical chemistry laboratory exersize. - -Install: - -``` -python setup.py build -python setup.py install -``` - -Requires C++ complier. - -### TODO - -- General code cleanup -- API level documentation -- Unit testing -- Add example lessons -- Add Monte-Carlo - -Contact: -arm61@bath.ac.uk diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..da52742 --- /dev/null +++ b/README.rst @@ -0,0 +1,39 @@ +pylj +==== + +what is pylj? +------------- + +pylj is an open-source library to facilitate student interaction with classical simulation. It is designed to operate within the Jupyter notebook framework, making it easy to implement in the classroom, or computer lab. Additionally, due to the open-source, and documented, nature of the code it is easy for educators to add unique, custom extensions. + +what does pylj offer? +--------------------- + +Currently pylj will perform the simulation of a 2D argon system by molecular dynamics, with both NVE and NVT ensembles available and making use of a Velocity-Verlet integrator. A series of sampling classes exist (found in the sample module), such as the Interactions and Scattering classes. However, it is straightforward to build a custom sampling class either from scratch or using the sampling class building tools. + +example exercises +----------------- + +We are currently in the process of developing example laboratory exercises that make use of pylj. These will include a study of ideal and non-ideal gas conditions and the effect of the phase transitions on the radial distribution function, scattering profiles and mean squared deviation. + +how do i get pylj? +------------------ + +If you are interested in using pylj, in any sense, fork the code at http://www.github.com/arm61/pylj or email Andrew (arm61 'at' bath.ac.uk). We are currently investigating the feasibility of hosting a freely available test instance on Amazon Web Services. + +requirements +------------ +To run pylj locally it is necessary to have: + +- python 3 +- numpy +- matplotlib +- cython +- C++ complier + +todo +---- +- unit testing +- complete example lesssons +- add Monte-Carlo +- add energy minimisation diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..b1e129f --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = python -msphinx +SPHINXPROJ = pylj +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/build/doctrees/environment.pickle b/docs/build/doctrees/environment.pickle new file mode 100644 index 0000000..129711d Binary files /dev/null and b/docs/build/doctrees/environment.pickle differ diff --git a/docs/build/doctrees/index.doctree b/docs/build/doctrees/index.doctree new file mode 100644 index 0000000..1a5ef73 Binary files /dev/null and b/docs/build/doctrees/index.doctree differ diff --git a/docs/build/doctrees/modules.doctree b/docs/build/doctrees/modules.doctree new file mode 100644 index 0000000..5913608 Binary files /dev/null and b/docs/build/doctrees/modules.doctree differ diff --git a/docs/build/doctrees/pylj.doctree b/docs/build/doctrees/pylj.doctree new file mode 100644 index 0000000..125a1c8 Binary files /dev/null and b/docs/build/doctrees/pylj.doctree differ diff --git a/docs/build/html/.buildinfo b/docs/build/html/.buildinfo new file mode 100644 index 0000000..42691c2 --- /dev/null +++ b/docs/build/html/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 3e24725cde270cd0e889a75e43e9746b +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html new file mode 100644 index 0000000..a848ec8 --- /dev/null +++ b/docs/build/html/_modules/index.html @@ -0,0 +1,104 @@ + + + + + + + Overview: module code — pylj 0.0.6a documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

All modules for which code is available

+ + +
+
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/pylj/examples.html b/docs/build/html/_modules/pylj/examples.html new file mode 100644 index 0000000..f8ab575 --- /dev/null +++ b/docs/build/html/_modules/pylj/examples.html @@ -0,0 +1,162 @@ + + + + + + + pylj.examples — pylj 0.0.6a documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for pylj.examples

+from pylj import md, comp, sample
+
+
[docs]def md_nvt(number_of_particles, temperature, box_length, number_of_steps, sample_frequency): + # Creates the visualisation environment + #%matplotlib notebook + # Initialise the system + system = md.initialise(number_of_particles, temperature, box_length, 'square') + # This sets the sampling class + sample_system = sample.Interactions(system) + # Start at time 0 + system.time = 0 + # Begin the molecular dynamics loop + for i in range(0, number_of_steps): + # At each step, calculate the forces on each particle + # and get acceleration + system.particles, system.distances, system.forces = comp.compute_forces(system.particles, + system.distances, + system.forces, system.box_length) + # Run the equations of motion integrator algorithm + system.particles = md.velocity_verlet(system.particles, system.timestep_length, system.box_length) + # Sample the thermodynamic and structural parameters of the system + system = md.sample(system.particles, system.box_length, system.initial_particles, system) + # Allow the system to interact with a heat bath + system.particles = comp.heat_bath(system.particles, system.temperature_sample, temperature) + # Iterate the time + system.time += system.timestep_length + system.step += 1 + # At a given frequency sample the positions and plot the RDF + if system.step % sample_frequency == 0: + sample_system.update(system) + return system
+ + +
[docs]def md_nve(number_of_particles, temperature, box_length, number_of_steps, sample_frequency): + # Creates the visualisation environment + #%matplotlib notebook + # Initialise the system + system = md.initialise(number_of_particles, temperature, box_length, 'square') + # This sets the sampling class + sample_system = sample.Interactions(system) + # Start at time 0 + system.time = 0 + # Begin the molecular dynamics loop + for i in range(0, number_of_steps): + # At each step, calculate the forces on each particle + # and get acceleration + system.particles, system.distances, system.forces = comp.compute_forces(system.particles, + system.distances, + system.forces, system.box_length) + # Run the equations of motion integrator algorithm + system.particles = md.velocity_verlet(system.particles, system.timestep_length, system.box_length) + # Sample the thermodynamic and structural parameters of the system + system = md.sample(system.particles, system.box_length, system.initial_particles, system) + # Iterate the time + system.time += system.timestep_length + system.step += 1 + # At a given frequency sample the positions and plot the RDF + if system.step % sample_frequency == 0: + sample_system.update(system) + return system
+ +
+ +
+
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/pylj/md.html b/docs/build/html/_modules/pylj/md.html new file mode 100644 index 0000000..c19891c --- /dev/null +++ b/docs/build/html/_modules/pylj/md.html @@ -0,0 +1,255 @@ + + + + + + + pylj.md — pylj 0.0.6a documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for pylj.md

+import numpy as np
+from pylj import comp, util
+
+
+
[docs]def initialise(number_of_particles, temperature, box_length, init_conf, timestep_length=5e-3): + """Initialise the particle positions (this can be either as a square or random arrangement), velocities (based on + the temperature defined, and calculate the initial forces/accelerations. + + Parameters + ---------- + number_of_particles: int + Number of particles to simulate. + temperature: float + Initial temperature of the particles, in Kelvin. + box_length: float + Length of a single dimension of the simulation square, in Angstrom. + init_conf: string, optional + The way that the particles are initially positioned. Should be one of: + + - 'square' + - 'random' + timestep_length: float (optional) + Length for each Velocity-Verlet integration step, in seconds. + + Returns + ------- + System + System information. + """ + system = util.System(number_of_particles, temperature, box_length, init_conf=init_conf, + timestep_length=timestep_length) + v = np.sqrt(2 * system.init_temp) + theta = 2 * np.pi * np.random.randn(system.particles.size) + system.particles['xvelocity'] = v * np.cos(theta) + system.particles['yvelocity'] = v * np.sin(theta) + system.particles, system.distances, system.forces = comp.compute_forces(system.particles, system.distances, + system.forces, system.box_length)
+ return system + +
[docs]def velocity_verlet(particles, timestep_length, box_length): + """Uses the Velocity-Verlet integrator to move forward in time. The + + Updates the particles positions and velocities in terms of the Velocity Verlet algorithm. Also calculates the + instanteous temperature, pressure, and force and appends these to the appropriate system array. + + Parameters + ---------- + particles: util.particle_dt, array_like + Information about the particles. + timestep_length: float + Length for each Velocity-Verlet integration step, in seconds. + box_length: float + Length of a single dimension of the simulation square, in Angstrom. + + Returns + ------- + util.particle_dt, array_like: + Information about the particles, with new positions and velocities. + """ + xposition_store = particles['xposition'] + yposition_store = particles['yposition'] + [particles['xposition'], particles['yposition']] = update_positions([particles['xposition'], + particles['yposition']], + [particles['xvelocity'], + particles['yvelocity']], + [particles['xacceleration'], + particles['yacceleration']], timestep_length, + box_length) + [particles['xvelocity'], particles['yvelocity']] = update_velocities([particles['xvelocity'], particles['yvelocity']], + [particles['xacceleration'], + particles['yacceleration']], timestep_length) + particles['xprevious_position'] = xposition_store + particles['yprevious_position'] = yposition_store
+ return particles + +
[docs]def sample(particles, box_length, initial_particles, system): + """Sample parameters of interest in the simulation. + + Parameters + ---------- + particles: util.particle_dt, array_like + Information about the particles. + box_length: float + Length of a single dimension of the simulation square, in Angstrom. + initial_particles: util.particle_dt, array-like + Information about the initial particle conformation. + system: System + Details about the whole system + + Returns + ------- + System: + Details about the whole system, with the new temperature, pressure, msd, and force appended to the appropriate + arrays. + """ + temperature_new = util.calculate_temperature(particles) + pressure_new = comp.calculate_pressure(particles, box_length, temperature_new) + msd_new = util.calculate_msd(particles, initial_particles, box_length) + system.temperature_sample = np.append(system.temperature_sample, temperature_new) + system.pressure_sample = np.append(system.pressure_sample, pressure_new) + system.force_sample = np.append(system.force_sample, np.sum(system.forces)) + system.msd_sample = np.append(system.msd_sample, msd_new)
+ return system + +
[docs]def update_positions(positions, velocities, accelerations, timestep_length, box_length): + """Update the particle positions using the Velocity-Verlet integrator. + + Parameters + ---------- + positions: (2, N) array_like + Where N is the number of particles, and the first row are the x positions and the second row the y positions. + velocities: (2, N) array_like + Where N is the number of particles, and the first row are the x velocities and the second row the y velocities. + accelerations: (2, N) array_like + Where N is the number of particles, and the first row are the x accelerations and the second row the y + accelerations. + timestep_length: float + Length for each Velocity-Verlet integration step, in seconds. + box_length: float + Length of a single dimension of the simulation square, in Angstrom. + + Returns + ------- + (2, N) array_like: + Updated positions. + """ + positions[0] += velocities[0] * timestep_length + 0.5 * accelerations[0] * timestep_length * timestep_length + positions[1] += velocities[1] * timestep_length + 0.5 * accelerations[1] * timestep_length * timestep_length + positions[0] = positions[0] % box_length + positions[1] = positions[1] % box_length
+ return [positions[0], positions[1]] + +
[docs]def update_velocities(velocities, accelerations, timestep_length): + """Update the particle velocities using the Velocity-Verlet algoritm. + + Parameters + ---------- + velocities: (2, N) array_like + Where N is the number of particles, and the first row are the x velocities and the second row the y velocities. + accelerations: (2, N) array_like + Where N is the number of particles, and the first row are the x accelerations and the second row the y + accelerations. + timestep_length: float + Length for each Velocity-Verlet integration step, in seconds. + + Returns + ------- + (2, N) array_like: + Updated velocities. + """ + velocities[0] += 0.5 * accelerations[0] * timestep_length + velocities[1] += 0.5 * accelerations[1] * timestep_length
+ return [velocities[0], velocities[1]] + +
+ +
+
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/pylj/sample.html b/docs/build/html/_modules/pylj/sample.html new file mode 100644 index 0000000..c152b5c --- /dev/null +++ b/docs/build/html/_modules/pylj/sample.html @@ -0,0 +1,558 @@ + + + + + + + pylj.sample — pylj 0.0.6a documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for pylj.sample

+import numpy as np
+import matplotlib.pyplot as plt
+
+
+
[docs]class Scattering(object): + """The Scattering class will plot the particle positions, radial distribution function, mean squared deviation and + scattering profile (as a fft of the rdf). This sampling class is ideal for observing the phase transitions between + solid, liquid, gas. + + Parameters + ---------- + system: System + The whole system information. + """ + def __init__(self, system): + fig, ax = environment(4) + self.average_rdf = [] + self.r = [] + self.average_diff = [] + self.q = [] + self.initial_pos = [system.particles['xposition'], system.particles['yposition']] + + setup_cellview(ax[0, 0], system) + setup_rdfview(ax[0, 1], system) + setup_diffview(ax[1, 1]) + setup_msdview(ax[1, 0]) + + plt.tight_layout() + self.ax = ax + self.fig = fig + +
[docs] def update(self, system): + """This updates the visualisation environment. Often this can be slower than the cythonised force calculation + so used is wisely. + + Parameters + ---------- + system: System + The whole system information. + """ + update_cellview(self.ax[0, 0], system) + update_rdfview(self.ax[0, 1], system, self.average_rdf, self.r) + update_diffview(self.ax[1, 1], system, self.average_diff, self.q) + update_msdview(self.ax[1, 0], system) + self.fig.canvas.draw()
+ +
[docs] def average(self): + gr = np.average(self.average_rdf, axis=0) + x = np.average(self.r, axis=0) + line = self.ax[0, 1].lines[0] + line.set_xdata(x) + line.set_ydata(gr) + self.ax[0, 1].set_ylim([0, np.amax(gr) + 0.5]) + self.fig.canvas.draw() + + iq = np.average(self.average_diff, axis=0) + x = np.average(self.q, axis=0) + line = self.ax[1, 1].lines[0] + line.set_ydata(iq) + line.set_xdata(x) + self.ax[1, 1].set_ylim([np.amin(iq) - np.amax(iq) * 0.05, np.amax(iq) + np.amax(iq) * 0.05]) + self.ax[1, 1].set_xlim([np.amin(x), np.amax(x)])
+ + +
[docs]class Interactions(object): + """The Interactions class will plot the particle positions, total force, simulation pressure and temperature. This + class is perfect for showing the interactions between the particles and therefore the behaviour of ideal gases and + deviation when the conditions of an ideal gas are not met. + + Parameters + ---------- + system: System + The whole system information. + """ + def __init__(self, system): + fig, ax = environment(4) + + setup_cellview(ax[0, 0], system) + setup_forceview(ax[0, 1]) + setup_pressureview(ax[1, 0]) + setup_tempview(ax[1, 1]) + + plt.tight_layout() + self.ax = ax + self.fig = fig + +
[docs] def update(self, system): + """This updates the visualisation environment. Often this can be slower than the cythonised force calculation + so used is wisely. + + Parameters + ---------- + system: System + The whole system information. + """ + update_cellview(self.ax[0, 0], system) + update_forceview(self.ax[0, 1], system) + update_tempview(self.ax[1, 1], system) + update_pressureview(self.ax[1, 0], system) + + self.fig.canvas.draw()
+ + +
[docs]class JustCell(object): + """The JustCell class will plot just the particles positions. This is a simplistic sampling class for quick + visualisation. + + Parameters + ---------- + system: System + The whole system information. + """ + def __init__(self, system): + fig, ax = environment(1) + + setup_cellview(ax, system) + + plt.tight_layout() + + self.ax = ax + self.fig = fig + +
[docs] def update(self, system): + """This updates the visualisation environment. Often this can be slower than the cythonised force calculation + so used is wisely. + + Parameters + ---------- + system: System + The whole system information. + """ + update_cellview(self.ax, system) + + self.fig.canvas.draw()
+ + +
[docs]class RDF(object): + """The RDF class will plot the particle positions and radial distribution function. This sampling class is can be + used to show the relative RDFs for solid, liquid, gas. + + Parameters + ---------- + system: System + The whole system information. + """ + def __init__(self, system): + fig, ax = environment(2) + self.average_rdf = [] + self.r = [] + self.average_diff = [] + self.q = [] + self.initial_pos = [system.particles['xposition'], system.particles['yposition']] + + setup_cellview(ax[0], system) + setup_rdfview(ax[1], system) + + plt.tight_layout() + self.ax = ax + self.fig = fig + +
[docs] def update(self, system): + """This updates the visualisation environment. Often this can be slower than the cythonised force calculation + so used is wisely. + + Parameters + ---------- + system: System + The whole system information. + """ + update_cellview(self.ax[0], system) + update_rdfview(self.ax[1], system, self.average_rdf, self.r) + self.fig.canvas.draw()
+ +
[docs] def average(self): + gr = np.average(self.average_rdf, axis=0) + x = np.average(self.r, axis=0) + line = self.ax[1].lines[0] + line.set_xdata(x) + line.set_ydata(gr) + self.ax[1].set_ylim([0, np.amax(gr) + 0.5]) + self.fig.canvas.draw()
+ + +
[docs]def environment(panes): + """The visualisation environment consists of a series of panes (1, 2, or 4 are allowed). This function allows the + number of panes in the visualisation to be defined. + + Parameters + ---------- + panes: int + Number of visualisation panes. + + Returns + ------- + Matplotlib.figure.Figure object: + The relevant Matplotlib figure. + Axes object or array of axes objects: + The axes related to each of the panes. For panes=1 this is a single object, for panes=2 it is a 1-D array and + for panes=4 it is a 2-D array. + """ + fig, ax = plt.subplot() + if panes == 1: + fig, ax = plt.subplots(figsize=(4, 4)) + elif panes == 2: + fig, ax = plt.subplots(1, 2, figsize=(8, 4)) + elif panes == 4: + fig, ax = plt.subplots(2, 2, figsize=(8, 8)) + else: + AttributeError('The only options for the number of panes are 1, 2, or 4') + return fig, ax
+ + +
[docs]def setup_cellview(ax, system): + """Builds the particle position visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + """ + xpos = system.particles['xposition'] + ypos = system.particles['yposition'] + mk = (1052.2 / (system.box_length - 0.78921) - 1.2174) + ax.plot(xpos, ypos, 'o', markersize=mk, markeredgecolor='black', color='#34a5daff') + ax.set_xlim([0, system.box_length]) + ax.set_ylim([0, system.box_length]) + ax.set_xticks([]) + ax.set_yticks([])
+ + +
[docs]def setup_forceview(ax): + """Builds the total force visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + """ + ax.plot([0], color='#34a5daff') + ax.set_ylabel('Force/N', fontsize=16) + ax.set_xlabel('Time/s', fontsize=16)
+ + +
[docs]def setup_rdfview(ax, system): + """Builds the radial distribution function visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + """ + ax.plot([0], color='#34a5daff') + ax.set_xlim([0, system.box_length/2]) + ax.set_yticks([]) + ax.set_ylabel('RDF', fontsize=16) + ax.set_xlabel('r/Å', fontsize=16)
+ + +
[docs]def setup_diffview(ax): + """Builds the scattering profile visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + """ + ax.plot([0], color='#34a5daff') + ax.set_yticks([]) + ax.set_ylabel('log(I[q])', fontsize=16) + ax.set_xlabel('log(q)/Å$^{-1}$', fontsize=16)
+ + +
[docs]def setup_pressureview(ax): + """Builds the simulation instantaneous pressure visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + """ + ax.plot([0], color='#34a5daff') + ax.set_ylabel('Pressure/Pa', fontsize=16) + ax.set_xlabel('Time/s', fontsize=16)
+ + +
[docs]def setup_tempview(ax): + """Builds the simulation instantaneous temperature visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + """ + ax.plot([0], color='#34a5daff') + ax.set_ylabel('Temperature/K', fontsize=16) + ax.set_xlabel('Time/s', fontsize=16)
+ + +
[docs]def update_cellview(ax, system): + """Updates the particle positions visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + """ + x3 = system.particles['xposition'] + y3 = system.particles['yposition'] + line = ax.lines[0] + line.set_ydata(y3) + line.set_xdata(x3)
+ + +
[docs]def update_rdfview(ax, system, average_rdf, r): + """Updates the radial distribution function visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + average_rdf: array_like + The radial distribution functions g(r) for each timestep, to later be averaged. + r: array_like + The radial distribution functions r for each timestep, to later be averaged. + """ + hist, bin_edges = np.histogram(system.distances, bins=np.linspace(0, system.box_length/2 + 0.5, 100)) + gr = hist / (system.number_of_particles * (system.number_of_particles / system.box_length ** 2) * np.pi * + (bin_edges[:-1] + 0.5 / 2.) * 0.5) + average_rdf.append(gr) + x = bin_edges[:-1] + 0.5 / 2 + r.append(x) + + line = ax.lines[0] + line.set_xdata(x) + line.set_ydata(gr) + ax.set_ylim([0, np.amax(gr) + np.amax(gr) * 0.05])
+ + +
[docs]def update_diffview(ax, system, average_diff, q): + """Updates the scattering profile visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + average_diff: array_like + The scattering profile's i(q) for each timestep, to later be averaged. + q: array_like + The scattering profile's q for each timestep, to later be averaged. + """ + hist, bin_edges = np.histogram(system.distances, bins=np.linspace(0, system.box_length/2 + 0.5, 100)) + gr = hist / (system.number_of_particles * (system.number_of_particles / system.box_length ** 2) * np.pi * + (bin_edges[:-1] + 0.5 / 2.) * 0.5) + x2 = np.log(np.fft.rfftfreq(len(gr))[5:]) + y2 = np.log(np.fft.rfft(gr)[5:]) + average_diff.append(y2) + q.append(x2) + line1 = ax.lines[0] + line1.set_xdata(x2) + line1.set_ydata(y2) + ax.set_ylim([np.amin(y2) - np.amax(y2) * 0.05, np.amax(y2) + np.amax(y2) * 0.05]) + ax.set_xlim([np.amin(x2), np.amax(x2)])
+ + +
[docs]def update_forceview(ax, system): + """Updates the total force visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + """ + line = ax.lines[0] + line.set_ydata(system.force_sample * 1e-10) + line.set_xdata(np.arange(0, system.step) * system.timestep_length) + ax.set_xlim(0, system.step * system.timestep_length) + ax.set_ylim(np.amin(system.force_sample * 1e-10)-np.amax(system.force_sample * 1e-10) * 0.05, + np.amax(system.force_sample * 1e-10)+np.amax(system.force_sample * 1e-10) * 0.05)
+ + +
[docs]def update_tempview(ax, system): + """Updates the simulation instantaneous temperature visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + """ + line = ax.lines[0] + line.set_ydata(system.temperature_sample) + line.set_xdata(np.arange(0, system.step) * system.timestep_length) + ax.set_xlim(0, system.step * system.timestep_length) + ax.set_ylim(np.amin(system.temperature_sample)-np.amax(system.temperature_sample) * 0.05, + np.amax(system.temperature_sample)+np.amax(system.temperature_sample) * 0.05)
+ + +
[docs]def update_pressureview(ax, system): + """Updates the simulation instantaneous pressure visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + """ + line = ax.lines[0] + line.set_ydata(system.pressure_sample * 1e10) + line.set_xdata(np.arange(0, system.step) * system.timestep_length) + ax.set_xlim(0, system.step * system.timestep_length) + ax.set_ylim(np.amin(system.pressure_sample * 1e10) - np.amax(system.pressure_sample * 1e10) * 0.05, + np.amax(system.pressure_sample * 1e10) + np.amax(system.pressure_sample * 1e10) * 0.05)
+ + +
[docs]def setup_msdview(ax): + """Builds the simulation mean squared deviation visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + """ + ax.plot([0], color='#34a5daff') + ax.set_ylabel('MSD/Å$^2$', fontsize=16) + ax.set_xlabel('Time/s', fontsize=16)
+ + +
[docs]def update_msdview(ax, system): + """Updates the simulation mean squared deviation visualisation pane. + + Parameters + ---------- + ax: Axes object + The axes position that the pane should be placed in. + system: System + The whole system information. + """ + line = ax.lines[0] + + line.set_ydata(system.msd_sample) + line.set_xdata(np.arange(0, system.step) * system.timestep_length) + ax.set_xlim(0, system.step * system.timestep_length) + ax.set_ylim(0, np.amax(system.msd_sample)+np.amax(system.msd_sample) * 0.05)
+
+ +
+
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/build/html/_modules/pylj/util.html b/docs/build/html/_modules/pylj/util.html new file mode 100644 index 0000000..ea8665b --- /dev/null +++ b/docs/build/html/_modules/pylj/util.html @@ -0,0 +1,265 @@ + + + + + + + pylj.util — pylj 0.0.6a documentation + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

Source code for pylj.util

+import numpy as np
+
+
+
[docs]class System: + """Simulation system. + + This class is designed to store all of the information about the job that is being run. This includes the particles + object, as will as sampling objects such as the temperature, pressure, etc. arrays. + + Parameters + ---------- + number_of_particles: int + Number of particles to simulate. + temperature: float + Initial temperature of the particles, in Kelvin. + box_length: float + Length of a single dimension of the simulation square, in Angstrom. + init_conf: string, optional + The way that the particles are initially positioned. Should be one of: + + - 'square' + - 'random' + timestep_length: float (optional) + Length for each Velocity-Verlet integration step, in seconds. + """ + def __init__(self, number_of_particles, temperature, box_length, init_conf='square', timestep_length=5e-3): + self.number_of_particles = number_of_particles + self.init_temp = temperature + if box_length <= 600: + self.box_length = box_length + else: + raise AttributeError('With a box length of {} the particles are probably too small to be seen in the ' + 'viewer. Try something (much) less than 600.'.format(box_length)) + self.timestep_length = timestep_length + self.particles = None + if init_conf == 'square': + self.square() + elif init_conf == 'random': + self.random() + else: + raise NotImplementedError('The initial configuration type {} is not recognised. ' + 'Available options are: square or random'.format(init_conf)) + self.step = 0 + self.time = 0. + self.distances = np.zeros(self.number_of_pairs()) + self.forces = np.zeros(self.number_of_pairs()) + self.temperature_sample = np.array([]) + self.pressure_sample = np.array([]) + self.force_sample = np.array([]) + self.msd_sample = np.array([]) + self.initial_particles = np.array(self.particles) + +
[docs] def number_of_pairs(self): + """Calculates the number of pairwise interactions in the simulation. + + Returns + ------- + int: + Number of pairwise interactions in the system. + """
+ return int((self.number_of_particles - 1) * self.number_of_particles / 2) + +
[docs] def square(self): + """Sets the initial positions of the particles on a square lattice. + """ + part_dt = particle_dt() + self.particles = np.zeros(self.number_of_particles, dtype=part_dt) + m = int(np.ceil(np.sqrt(self.number_of_particles))) + d = self.box_length / m + n = 0 + for i in range(0, m): + for j in range(0, m): + if n < self.number_of_particles: + self.particles[n]['xposition'] = (i + 0.5) * d + self.particles[n]['yposition'] = (j + 0.5) * d
+ n += 1 + +
[docs] def random(self): + """Sets the initial positions of the particles in a random arrangement. + """ + part_dt = particle_dt() + self.particles = np.zeros(self.number_of_particles, dtype=part_dt) + self.particles['xposition'] = np.random.uniform(0, self.box_length, self.number_of_particles)
+ self.particles['yposition'] = np.random.uniform(0, self.box_length, self.number_of_particles) + +
[docs]def pbc_correction(position, cell): + """Correct for the periodic boundary condition. + + Parameters + ---------- + position: float + Particle position. + cell: float + Cell vector. + + Returns + ------- + float: + Corrected particle position.""" + if np.abs(position) > 0.5 * cell: + position *= 1 - cell / np.abs(position)
+ return position + +
[docs]def calculate_temperature(particles): + """Determine the instantaneous temperature of the system. + + Parameters + ---------- + particles: util.particle_dt, array_like + Information about the particles. + + Returns + ------- + float: + Calculated instantaneous simulation temperature. + """ + k = 0 + for i in range(0, particles['xposition'].size): + v = np.sqrt(particles['xvelocity'][i] * particles['xvelocity'][i] + particles['yvelocity'][i] * + particles['yvelocity'][i]) + k += 0.5 * v * v
+ return k / particles['xposition'].size + +
[docs]def calculate_msd(particles, initial_particles, box_length): + """Determines the mean squared displacement of the particles in the system. + + Parameters + ---------- + particles: util.particle_dt, array_like + Information about the particles. + initial_particles: util.particle_dt, array_like + Information about the initial state of the particles. + box_length: float + Size of the cell vector. + + Returns + ------- + float: + Mean squared deviation for the particles at the given timestep. + """ + dx = particles['xposition'] - initial_particles['xposition'] + dy = particles['yposition'] - initial_particles['yposition'] + for i in range(0, particles['xposition'].size): + if (np.abs(dx[i]) > 0.5 * box_length): + dx[i] *= 1 - box_length / np.abs(dx[i]) + if (np.abs(dy[i]) > 0.5 * box_length): + dy[i] *= 1 - box_length / np.abs(dy[i]) + dr = np.sqrt(dx * dx + dy * dy)
+ return np.average(dr ** 2) + +
[docs]def particle_dt(): + """Builds the data type for the particles, this consists of: + + - xposition and yposition + - xvelocity and yvelocity + - xacceleration and yacceleration + - xprevious_position and ypresvious_position + - xforce and yforce + - energy + """ + return np.dtype([('xposition', np.float64), ('yposition', np.float64), ('xvelocity', np.float64), + ('yvelocity', np.float64), ('xacceleration', np.float64), ('yacceleration', np.float64), + ('xprevious_position', np.float64), ('yprevious_position', np.float64), ('xforce', np.float64),
+ ('yforce', np.float64), ('energy', np.float64)]) +
+ +
+
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/build/html/_sources/index.rst.txt b/docs/build/html/_sources/index.rst.txt new file mode 100644 index 0000000..2fb6787 --- /dev/null +++ b/docs/build/html/_sources/index.rst.txt @@ -0,0 +1,22 @@ +.. pylj documentation master file, created by + sphinx-quickstart on Tue May 15 00:23:01 2018. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to pylj's documentation! +================================ + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + modules + +.. include:: ../README.rst + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/build/html/_sources/modules.rst.txt b/docs/build/html/_sources/modules.rst.txt new file mode 100644 index 0000000..2afb111 --- /dev/null +++ b/docs/build/html/_sources/modules.rst.txt @@ -0,0 +1,7 @@ +pylj +==== + +.. toctree:: + :maxdepth: 4 + + pylj diff --git a/docs/build/html/_sources/pylj.rst.txt b/docs/build/html/_sources/pylj.rst.txt new file mode 100644 index 0000000..d794622 --- /dev/null +++ b/docs/build/html/_sources/pylj.rst.txt @@ -0,0 +1,46 @@ +pylj package +============ + +Submodules +---------- + +pylj\.util module +----------------- + +.. automodule:: pylj.util + :members: + :undoc-members: + :show-inheritance: + +pylj\.md module +--------------- + +.. automodule:: pylj.md + :members: + :undoc-members: + :show-inheritance: + +pylj\.sample module +------------------- + +.. automodule:: pylj.sample + :members: + :undoc-members: + :show-inheritance: + +pylj\.examples module +--------------------- + +.. automodule:: pylj.examples + :members: + :undoc-members: + :show-inheritance: + +pylj\.comp module +------------------- + +.. automodule:: pylj.comp + :members: + :undoc-members: + :show-inheritance: + diff --git a/docs/build/html/_static/ajax-loader.gif b/docs/build/html/_static/ajax-loader.gif new file mode 100644 index 0000000..61faf8c Binary files /dev/null and b/docs/build/html/_static/ajax-loader.gif differ diff --git a/docs/build/html/_static/alabaster.css b/docs/build/html/_static/alabaster.css new file mode 100644 index 0000000..be65b13 --- /dev/null +++ b/docs/build/html/_static/alabaster.css @@ -0,0 +1,693 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro', serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: 'Garamond', 'Georgia', serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro', serif; + font-size: 1em; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Garamond', 'Georgia', serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: 'Garamond', 'Georgia', serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Make nested-list/multi-paragraph items look better in Releases changelog + * pages. Without this, docutils' magical list fuckery causes inconsistent + * formatting between different release sub-lists. + */ +div#changelog > div.section > ul > li > p:only-child { + margin-bottom: 0; +} + +/* Hide fugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} \ No newline at end of file diff --git a/docs/build/html/_static/basic.css b/docs/build/html/_static/basic.css new file mode 100644 index 0000000..6df76b0 --- /dev/null +++ b/docs/build/html/_static/basic.css @@ -0,0 +1,639 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 170px; +} + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li div.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px 7px 0 7px; + background-color: #ffe; + width: 40%; + float: right; +} + +p.sidebar-title { + font-weight: bold; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px 7px 0 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +div.admonition dl { + margin-bottom: 0; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + border: 0; + border-collapse: collapse; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +dl { + margin-bottom: 15px; +} + +dd p { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dt:target, .highlighted { + background-color: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; +} + +td.linenos pre { + padding: 5px 0px; + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + margin-left: 0.5em; +} + +table.highlighttable td { + padding: 0 0.5em 0 0.5em; +} + +div.code-block-caption { + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +div.code-block-caption + div > div.highlight > pre { + margin-top: 0; +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + padding: 1em 1em 0; +} + +div.literal-block-wrapper div.highlight { + margin: 0; +} + +code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +code.descclassname { + background-color: transparent; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: relative; + left: 0px; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/docs/build/html/_static/comment-bright.png b/docs/build/html/_static/comment-bright.png new file mode 100644 index 0000000..15e27ed Binary files /dev/null and b/docs/build/html/_static/comment-bright.png differ diff --git a/docs/build/html/_static/comment-close.png b/docs/build/html/_static/comment-close.png new file mode 100644 index 0000000..4d91bcf Binary files /dev/null and b/docs/build/html/_static/comment-close.png differ diff --git a/docs/build/html/_static/comment.png b/docs/build/html/_static/comment.png new file mode 100644 index 0000000..dfbc0cb Binary files /dev/null and b/docs/build/html/_static/comment.png differ diff --git a/docs/build/html/_static/custom.css b/docs/build/html/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/docs/build/html/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js new file mode 100644 index 0000000..5654977 --- /dev/null +++ b/docs/build/html/_static/doctools.js @@ -0,0 +1,287 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + */ +jQuery.urldecode = function(x) { + return decodeURIComponent(x).replace(/\+/g, ' '); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s == 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node) { + if (node.nodeType == 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) { + var span = document.createElement("span"); + span.className = className; + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this); + }); + } + } + return this.each(function() { + highlight(this); + }); +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated == 'undefined') + return string; + return (typeof translated == 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated == 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) == 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this == '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + $(document).keyup(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box or textarea + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') { + switch (event.keyCode) { + case 37: // left + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + case 39: // right + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); \ No newline at end of file diff --git a/docs/build/html/_static/documentation_options.js b/docs/build/html/_static/documentation_options.js new file mode 100644 index 0000000..7c937f3 --- /dev/null +++ b/docs/build/html/_static/documentation_options.js @@ -0,0 +1,9 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: '', + VERSION: '0.0.6a', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + FILE_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt' +}; \ No newline at end of file diff --git a/docs/build/html/_static/down-pressed.png b/docs/build/html/_static/down-pressed.png new file mode 100644 index 0000000..5756c8c Binary files /dev/null and b/docs/build/html/_static/down-pressed.png differ diff --git a/docs/build/html/_static/down.png b/docs/build/html/_static/down.png new file mode 100644 index 0000000..1b3bdad Binary files /dev/null and b/docs/build/html/_static/down.png differ diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/docs/build/html/_static/file.png differ diff --git a/docs/build/html/_static/jquery-3.1.0.js b/docs/build/html/_static/jquery-3.1.0.js new file mode 100644 index 0000000..f2fc274 --- /dev/null +++ b/docs/build/html/_static/jquery-3.1.0.js @@ -0,0 +1,10074 @@ +/*eslint-disable no-unused-vars*/ +/*! + * jQuery JavaScript Library v3.1.0 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2016-07-07T21:44Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + + + + function DOMEval( code, doc ) { + doc = doc || document; + + var script = doc.createElement( "script" ); + + script.text = code; + doc.head.appendChild( script ).parentNode.removeChild( script ); + } +/* global Symbol */ +// Defining this global in .eslintrc would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.1.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); + }, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE <=9 - 11, Edge 12 - 13 + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.0 + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-01-04 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + // Known :disabled false positives: + // IE: *[disabled]:not(button, input, select, textarea, optgroup, option, menuitem, fieldset) + // not IE: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Check form elements and option elements for explicit disabling + return "label" in elem && elem.disabled === disabled || + "form" in elem && elem.disabled === disabled || + + // Check non-disabled form elements for fieldset[disabled] ancestors + "form" in elem && elem.disabled === false && ( + // Support: IE6-11+ + // Ancestry is covered for us + elem.isDisabled === disabled || + + // Otherwise, assume any non-