# ---------------------------------------------------------------------- # LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator # https://www.lammps.org/ Sandia National Laboratories # LAMMPS Development team: developers@lammps.org # # Copyright (2003) Sandia Corporation. Under the terms of Contract # DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains # certain rights in this software. This software is distributed under # the GNU General Public License. # # See the README file in the top-level LAMMPS directory. # ------------------------------------------------------------------------- ################################################################################ # Alternative Python Wrapper # Written by Richard Berger ################################################################################ # for python2/3 compatibility from __future__ import print_function import io import os import re import sys import tempfile from collections import namedtuple from lammps.core import lammps from lammps.constants import LMP_VAR_EQUAL, LMP_VAR_ATOM, LMP_VAR_VECTOR, LMP_VAR_STRING # ------------------------------------------------------------------------- class OutputCapture(object): """ Utility class to capture LAMMPS library output """ def __init__(self): self.stdout_fd = 1 self.captured_output = "" def __enter__(self): self.tmpfile = tempfile.TemporaryFile(mode='w+b') sys.stdout.flush() # make copy of original stdout self.stdout_orig = os.dup(self.stdout_fd) # replace stdout and redirect to temp file os.dup2(self.tmpfile.fileno(), self.stdout_fd) return self def __exit__(self, exc_type, exc_value, traceback): os.dup2(self.stdout_orig, self.stdout_fd) os.close(self.stdout_orig) self.tmpfile.close() @property def output(self): sys.stdout.flush() self.tmpfile.flush() self.tmpfile.seek(0, io.SEEK_SET) self.captured_output = self.tmpfile.read().decode('utf-8') return self.captured_output # ------------------------------------------------------------------------- class Variable(object): def __init__(self, pylammps_instance, name): self._pylmp = pylammps_instance self.name = name @property def style(self): vartype = self._pylmp.lmp.lib.lammps_extract_variable_datatype(self._pylmp.lmp.lmp, self.name.encode()) if vartype == LMP_VAR_EQUAL: return "equal" elif vartype == LMP_VAR_ATOM: return "atom" elif vartype == LMP_VAR_VECTOR: return "vector" elif vartype == LMP_VAR_STRING: return "string" return None @property def value(self): return self._pylmp.lmp.extract_variable(self.name) @value.setter def value(self, newvalue): style = self.style if style == "equal" or style == "string": self._pylmp.variable("{} {} {}".format(self.name, style, newvalue)) else: raise Exception("Setter not implemented for {} style variables.".format(style)) def __str__(self): value = self.value if isinstance(value, str): value = "\"{}\"".format(value) return "Variable(name=\"{}\", value={})".format(self.name, value) def __repr__(self): return self.__str__() # ------------------------------------------------------------------------- class AtomList(object): """ A dynamic list of atoms that returns either an :py:class:`Atom` or :py:class:`Atom2D` instance for each atom. Instances are only allocated when accessed. :ivar natoms: total number of atoms :ivar dimensions: number of dimensions in system """ def __init__(self, pylammps_instance): self._pylmp = pylammps_instance self.natoms = self._pylmp.system.natoms self.dimensions = self._pylmp.system.dimensions self._loaded = {} def __getitem__(self, index): """ Return Atom with given local index :param index: Local index of atom :type index: int :rtype: Atom or Atom2D """ if index not in self._loaded: if self.dimensions == 2: atom = Atom2D(self._pylmp, index) else: atom = Atom(self._pylmp, index) self._loaded[index] = atom return self._loaded[index] def __len__(self): return self.natoms # ------------------------------------------------------------------------- class Atom(object): """ A wrapper class then represents a single atom inside of LAMMPS It provides access to properties of the atom and allows you to change some of them. """ def __init__(self, pylammps_instance, index): self._pylmp = pylammps_instance self.index = index def __dir__(self): return [k for k in super().__dir__() if not k.startswith('_')] def get(self, name, index): prop = self._pylmp.lmp.numpy.extract_atom(name) if prop is not None: return prop[index] return None @property def id(self): """ Return the atom ID :type: int """ return self.get("id", self.index) @property def type(self): """ Return the atom type :type: int """ return self.get("type", self.index) @property def mol(self): """ Return the atom molecule index :type: int """ return self.get("mol", self.index) @property def mass(self): """ Return the atom mass as a per-atom property. This returns either the per-type mass or the per-atom mass (AKA 'rmass') depending on what is available with preference for the per-atom mass. .. versionchanged:: 17Apr2024 Support both per-type and per-atom masses. With per-type return "mass[type[i]]" else return "rmass[i]". Per-atom mass is preferred if available. :type: float """ if self._pylmp.lmp.extract_setting('rmass_flag'): return self.get("rmass", self.index) else: return self.get("mass", self.type) @property def radius(self): """ Return the particle radius :type: float """ return self.get("radius", self.index) @property def position(self): """ :getter: Return position of atom :setter: Set position of atom :type: numpy.array (float, float, float) """ return self.get("x", self.index) @position.setter def position(self, value): current = self.position current[:] = value @property def velocity(self): """ :getter: Return velocity of atom :setter: Set velocity of atom :type: numpy.array (float, float, float) """ return self.get("v", self.index) @velocity.setter def velocity(self, value): current = self.velocity current[:] = value @property def force(self): """ Return the total force acting on the atom :type: numpy.array (float, float, float) """ return self.get("f", self.index) @force.setter def force(self, value): current = self.force current[:] = value @property def torque(self): """ Return the total torque acting on the atom :type: numpy.array (float, float, float) """ return self.get("torque", self.index) @force.setter def torque(self, value): current = self.torque current[:] = value @property def omega(self): """ Return the rotational velocity of the particle :type: numpy.array (float, float, float) """ return self.get("torque", self.index) @omega.setter def omega(self, value): current = self.torque current[:] = value @property def torque(self): """ Return the total torque acting on the particle :type: numpy.array (float, float, float) """ return self.get("torque", self.index) @torque.setter def torque(self, value): current = self.torque current[:] = value @property def angular_momentum(self): """ Return the angular momentum of the particle :type: numpy.array (float, float, float) """ return self.get("angmom", self.index) @angular_momentum.setter def angular_momentum(self, value): current = self.angular_momentum current[:] = value @property def charge(self): """ Return the atom charge :type: float """ return self.get("q", self.index) # ------------------------------------------------------------------------- class Atom2D(Atom): """ A wrapper class then represents a single 2D atom inside of LAMMPS Inherits all properties from the :py:class:`Atom` class, but returns 2D versions of position, velocity, and force. It provides access to properties of the atom and allows you to change some of them. """ def __init__(self, pylammps_instance, index): super(Atom2D, self).__init__(pylammps_instance, index) @property def position(self): """Access to coordinates of an atom :getter: Return position of atom :setter: Set position of atom :type: numpy.array (float, float) """ return super(Atom2D, self).position[0:2] @position.setter def position(self, value): current = self.position current[:] = value @property def velocity(self): """Access to velocity of an atom :getter: Return velocity of atom :setter: Set velocity of atom :type: numpy.array (float, float) """ return super(Atom2D, self).velocity[0:2] @velocity.setter def velocity(self, value): current = self.velocity current[:] = value @property def force(self): """Access to force of an atom :getter: Return force of atom :setter: Set force of atom :type: numpy.array (float, float) """ return super(Atom2D, self).force[0:2] @force.setter def force(self, value): current = self.force current[:] = value # ------------------------------------------------------------------------- class variable_set: def __init__(self, name, variable_dict): self._name = name array_pattern = re.compile(r"(?P.+)\[(?P[0-9]+)\]") for key, value in variable_dict.items(): m = array_pattern.match(key) if m: g = m.groupdict() varname = g['arr'] idx = int(g['index']) if varname not in self.__dict__: self.__dict__[varname] = {} self.__dict__[varname][idx] = value else: self.__dict__[key] = value def __str__(self): return "{}({})".format(self._name, ','.join(["{}={}".format(k, self.__dict__[k]) for k in self.__dict__.keys() if not k.startswith('_')])) def __dir__(self): return [k for k in self.__dict__.keys() if not k.startswith('_')] def __repr__(self): return self.__str__() # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- class PyLammps(object): """ This is a Python wrapper class around the lower-level :py:class:`lammps` class, exposing a more Python-like, object-oriented interface for prototyping systems inside of IPython and Jupyter notebooks. It either creates its own instance of :py:class:`lammps` or can be initialized with an existing instance. The arguments are the same of the lower-level interface. The original interface can still be accessed via :py:attr:`PyLammps.lmp`. .. deprecated:: TBA :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) :type name: string :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. :type cmdargs: list :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. :type ptr: pointer :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. :type comm: MPI_Comm :param verbose: print all LAMMPS output to stdout :type verbose: bool :ivar lmp: instance of original LAMMPS Python interface :vartype lmp: :py:class:`lammps` :ivar runs: list of completed runs, each storing the thermo output :vartype run: list """ def __init__(self, name="", cmdargs=None, ptr=None, comm=None, verbose=False): self.has_echo = False self.verbose = verbose if cmdargs: if '-echo' in cmdargs: idx = cmdargs.index('-echo') # ensures that echo line is ignored during output capture self.has_echo = idx+1 < len(cmdargs) and cmdargs[idx+1] in ('screen', 'both') if ptr: if isinstance(ptr,PyLammps): self.lmp = ptr.lmp elif isinstance(ptr,lammps): self.lmp = ptr else: self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) else: self.lmp = lammps(name=name,cmdargs=cmdargs,ptr=None,comm=comm) self.comm_nprocs = self.lmp.extract_setting("world_size") self.comm_me = self.lmp.extract_setting("world_rank") if self.comm_me == 0: print("\nWARNING-WARNING-WARNING-WARNING-WARNING-WARNING-WARNING-WARNING-WARNING-WARNING") print("WARNING:") print("WARNING: The PyLammps class is obsolete and will be removed from LAMMPS soon.") print("WARNING: Please use the lammps Python class instead.") print("WARNING:") print("WARNING-WARNING-WARNING-WARNING-WARNING-WARNING-WARNING-WARNING-WARNING-WARNING\n") print("LAMMPS output is captured by PyLammps wrapper") if self.comm_nprocs > 1: print("WARNING: Using PyLammps with multiple MPI ranks is experimental. Not all functionality is supported.") self._cmd_history = [] self._enable_cmd_history = False self.runs = [] if not self.lmp.has_package("PYTHON"): if self.comm_me == 0: print("WARNING: run thermo data not captured since PYTHON LAMMPS package is not enabled") def __enter__(self): return self def __exit__(self, ex_type, ex_value, ex_traceback): self.close() def __del__(self): if self.lmp: self.lmp.close() self.lmp = None def close(self): """Explicitly delete a LAMMPS instance This is a wrapper around the :py:meth:`lammps.close` of the Python interface. """ if self.lmp: self.lmp.close() self.lmp = None def version(self): """Return a numerical representation of the LAMMPS version in use. This is a wrapper around the :py:meth:`lammps.version` function of the Python interface. :return: version number :rtype: int """ return self.lmp.version() def file(self, file): """Read LAMMPS commands from a file. This is a wrapper around the :py:meth:`lammps.file` function of the Python interface. :param path: Name of the file/path with LAMMPS commands :type path: string """ self.lmp.file(file) @property def enable_cmd_history(self): """ :getter: Return whether command history is saved :setter: Set if command history should be saved :type: bool """ return self._enable_cmd_history @enable_cmd_history.setter def enable_cmd_history(self, value): """ :getter: Return whether command history is saved :setter: Set if command history should be saved :type: bool """ self._enable_cmd_history = (value == True) def write_script(self, filepath): """ Write LAMMPS script file containing all commands executed up until now :param filepath: path to script file that should be written :type filepath: string """ with open(filepath, "w") as f: for cmd in self._cmd_history: print(cmd, file=f) def clear_cmd_history(self): """ Clear LAMMPS command history up to this point """ self._cmd_history = [] def append_cmd_history(self, cmd): """ Commands will be added to the command history but not executed. Add `commands` only to the command history, but do not execute them, so that you can conveniently create LAMMPS input files, using :py:meth:`PyLammps.write_script()`. """ self._cmd_history.append(cmd) def command(self, cmd): """ Execute LAMMPS command If :py:attr:`PyLammps.enable_cmd_history` is set to ``True``, commands executed will be recorded. The entire command history can be written to a file using :py:meth:`PyLammps.write_script()`. To clear the command history, use :py:meth:`PyLammps.clear_cmd_history()`. :param cmd: command string that should be executed :type: cmd: string """ self.lmp.command(cmd) if self.enable_cmd_history: self._cmd_history.append(cmd) def _append_run_thermo(self, thermo): for k, v in thermo.items(): if k in self._current_run: self._current_run[k].append(v) else: self._current_run[k] = [v] def run(self, *args, **kwargs): """ Execute LAMMPS run command with given arguments Thermo data of the run is recorded and saved as new entry in :py:attr:`PyLammps.runs`. The latest run can be retrieved by :py:attr:`PyLammps.last_run`. Note, for recording of all thermo steps during a run, the PYTHON package needs to be enabled in LAMMPS. Otherwise, it will only capture the final timestep. """ self._current_run = {} self._last_thermo_step = -1 def end_of_step_callback(lmp): if self.lmp.last_thermo_step == self._last_thermo_step: return thermo = self.lmp.last_thermo() self._append_run_thermo(thermo) self._last_thermo_step = thermo['Step'] import __main__ __main__._PyLammps_end_of_step_callback = end_of_step_callback capture_thermo = self.lmp.has_package("PYTHON") if capture_thermo: self.fix("__pylammps_internal_run_callback", "all", "python/invoke", "1", "end_of_step", "_PyLammps_end_of_step_callback") output = self.__getattr__('run')(*args, **kwargs) if capture_thermo: self.unfix("__pylammps_internal_run_callback") self._append_run_thermo(self.lmp.last_thermo()) thermo_data = variable_set('ThermoData', self._current_run) r = {'thermo' : thermo_data } self.runs.append(namedtuple('Run', list(r.keys()))(*list(r.values()))) return output @property def last_run(self): """ Return data produced of last completed run command :getter: Returns an object containing information about the last run command :type: dict """ if len(self.runs) > 0: return self.runs[-1] return None @property def atoms(self): """ All atoms of this LAMMPS instance :getter: Returns a list of atoms currently in the system :type: AtomList """ return AtomList(self) @property def system(self): """ The system state of this LAMMPS instance :getter: Returns an object with properties storing the current system state :type: namedtuple """ output = self.lmp_info("system") output = output[output.index("System information:")+1:] d = self._parse_info_system(output) return namedtuple('System', d.keys())(*d.values()) @property def communication(self): """ The communication state of this LAMMPS instance :getter: Returns an object with properties storing the current communication state :type: namedtuple """ output = self.lmp_info("communication") output = output[output.index("Communication information:")+1:] d = self._parse_info_communication(output) return namedtuple('Communication', d.keys())(*d.values()) @property def computes(self): """ The list of active computes of this LAMMPS instance :getter: Returns a list of computes that are currently active in this LAMMPS instance :type: list """ output = self.lmp_info("computes") output = output[output.index("Compute information:")+1:] return self._parse_element_list(output) @property def dumps(self): """ The list of active dumps of this LAMMPS instance :getter: Returns a list of dumps that are currently active in this LAMMPS instance :type: list """ output = self.lmp_info("dumps") output = output[output.index("Dump information:")+1:] return self._parse_element_list(output) @property def fixes(self): """ The list of active fixes of this LAMMPS instance :getter: Returns a list of fixes that are currently active in this LAMMPS instance :type: list """ output = self.lmp_info("fixes") output = output[output.index("Fix information:")+1:] return self._parse_element_list(output) @property def groups(self): """ The list of active atom groups of this LAMMPS instance :getter: Returns a list of atom groups that are currently active in this LAMMPS instance :type: list """ return self.lmp.available_ids("group") @property def variables(self): """ Returns a dictionary of all variables defined in the current LAMMPS instance :getter: Returns a dictionary of all variables that are defined in this LAMMPS instance :type: dict """ variables = {} for name in self.lmp.available_ids("variable"): variables[name] = Variable(self, name) return variables def eval(self, expr): """ Evaluate LAMMPS input file expression. This is equivalent to using immediate variable expressions in the format "$(...)" in the LAMMPS input and will return the result of that expression. .. warning:: This function is only supported on MPI rank 0. Calling it from a different MPI rank will raise an exception. :param expr: the expression string that should be evaluated inside of LAMMPS :type expr: string :return: the value of the evaluated expression :rtype: float if numeric, string otherwise """ if self.comm_me > 0: raise Exception("PyLammps.eval() may only be used on MPI rank 0") value = self.lmp_print('"$(%s)"' % expr).strip() try: return float(value) except ValueError: return value def _split_values(self, line): return [x.strip() for x in line.split(',')] def _get_pair(self, value): return [x.strip() for x in value.split('=')] def _parse_info_system(self, output): system = {} system['dimensions'] = self.lmp.extract_setting("dimension") system['xlo'] = self.lmp.extract_global("boxxlo") system['ylo'] = self.lmp.extract_global("boxylo") system['zlo'] = self.lmp.extract_global("boxzlo") system['xhi'] = self.lmp.extract_global("boxxhi") system['yhi'] = self.lmp.extract_global("boxyhi") system['zhi'] = self.lmp.extract_global("boxzhi") xprd = system["xhi"] - system["xlo"] yprd = system["yhi"] - system["ylo"] zprd = system["zhi"] - system["zlo"] if self.lmp.extract_setting("triclinic") == 1: system['triclinic_box'] = (xprd, yprd, zprd) else: system['orthogonal_box'] = (xprd, yprd, zprd) system['nangles'] = self.lmp.extract_global("nbonds") system['nangletypes'] = self.lmp.extract_setting("nbondtypes") system['angle_style'] = self.lmp.extract_global("angle_style") system['nbonds'] = self.lmp.extract_global("nbonds") system['nbondtypes'] = self.lmp.extract_setting("nbondtypes") system['bond_style'] = self.lmp.extract_global("bond_style") system['ndihedrals'] = self.lmp.extract_global("ndihedrals") system['ndihedraltypes'] = self.lmp.extract_setting("ndihedraltypes") system['dihedral_style'] = self.lmp.extract_global("dihedral_style") system['nimpropers'] = self.lmp.extract_global("nimpropers") system['nimpropertypes'] = self.lmp.extract_setting("nimpropertypes") system['improper_style'] = self.lmp.extract_global("improper_style") system['kspace_style'] = self.lmp.extract_global("kspace_style") system['natoms'] = self.lmp.extract_global("natoms") system['ntypes'] = self.lmp.extract_global("ntypes") system['pair_style'] = self.lmp.extract_global("pair_style") system['atom_style'] = self.lmp.extract_global("atom_style") system['units'] = self.lmp.extract_global("units") for line in output: if line.startswith("Atom map"): system['atom_map'] = self._get_pair(line)[1] elif line.startswith("Boundaries"): system['boundaries'] = self._get_pair(line)[1] elif line.startswith("Molecule type"): system['molecule_type'] = self._get_pair(line)[1] return system def _parse_info_communication(self, output): comm = {} comm['nprocs'] = self.lmp.extract_setting("world_size") comm['nthreads'] = self.lmp.extract_setting("nthreads") comm['proc_grid'] = comm['procgrid'] = self.lmp.extract_global("procgrid") idx = self.lmp.extract_setting("comm_style") comm['comm_style'] = ('brick', 'tiled')[idx] idx = self.lmp.extract_setting("comm_style") comm['comm_layout'] = ('uniform', 'nonuniform', 'irregular')[idx] comm['ghost_velocity'] = self.lmp.extract_setting("ghost_velocity") == 1 for line in output: if line.startswith("MPI library"): comm['mpi_version'] = line.split(':')[1].strip() return comm def _parse_element_list(self, output): elements = [] for line in output: if not line or (":" not in line): continue element_info = self._split_values(line.split(':')[1].strip()) element = {'name': element_info[0]} for key, value in [self._get_pair(x) for x in element_info[1:]]: element[key] = value elements.append(element) return elements def lmp_print(self, s): """ needed for Python2 compatibility, since print is a reserved keyword """ return self.__getattr__("print")(s) def __dir__(self): return sorted(set(['angle_coeff', 'angle_style', 'atom_modify', 'atom_style', 'atom_style', 'bond_coeff', 'bond_style', 'boundary', 'change_box', 'communicate', 'compute', 'create_atoms', 'create_box', 'delete_atoms', 'delete_bonds', 'dielectric', 'dihedral_coeff', 'dihedral_style', 'dimension', 'dump', 'fix', 'fix_modify', 'group', 'improper_coeff', 'improper_style', 'include', 'kspace_modify', 'kspace_style', 'lattice', 'mass', 'minimize', 'min_style', 'neighbor', 'neigh_modify', 'newton', 'nthreads', 'pair_coeff', 'pair_modify', 'pair_style', 'processors', 'read', 'read_data', 'read_restart', 'region', 'replicate', 'reset_timestep', 'restart', 'run', 'run_style', 'thermo', 'thermo_modify', 'thermo_style', 'timestep', 'undump', 'unfix', 'units', 'variable', 'velocity', 'write_restart'] + self.lmp.available_styles("command"))) def lmp_info(self, s): # skip anything before and after Info-Info-Info # also skip timestamp line output = self.__getattr__("info")(s) indices = [index for index, line in enumerate(output) if line.startswith("Info-Info-Info-Info")] start = indices[0] end = indices[1] return [line for line in output[start+2:end] if line] def __getattr__(self, name): """ This method is where the Python 'magic' happens. If a method is not defined by the class PyLammps, it assumes it is a LAMMPS command. It takes all the arguments, concatinates them to a single string, and executes it using :py:meth:`lammps.PyLammps.command()`. :param verbose: Print output of command :type verbose: bool :return: line or list of lines of output, None if no output :rtype: list or string """ def handler(*args, **kwargs): cmd_args = [name] + [str(x) for x in args] self.lmp.flush_buffers() with OutputCapture() as capture: cmd = ' '.join(cmd_args) self.command(cmd) self.lmp.flush_buffers() output = capture.output comm = self.lmp.get_mpi_comm() if comm: output = self.lmp.comm.bcast(output, root=0) if self.verbose or ('verbose' in kwargs and kwargs['verbose']): print(output, end = '') lines = output.splitlines() if self.has_echo: lines = lines[1:] if len(lines) > 1: return lines elif len(lines) == 1: return lines[0] return None return handler class IPyLammps(PyLammps): """ IPython wrapper for LAMMPS which adds embedded graphics capabilities to PyLammps interface It either creates its own instance of :py:class:`lammps` or can be initialized with an existing instance. The arguments are the same of the lower-level interface. The original interface can still be accessed via :py:attr:`PyLammps.lmp`. :param name: "machine" name of the shared LAMMPS library ("mpi" loads ``liblammps_mpi.so``, "" loads ``liblammps.so``) :type name: string :param cmdargs: list of command line arguments to be passed to the :cpp:func:`lammps_open` function. The executable name is automatically added. :type cmdargs: list :param ptr: pointer to a LAMMPS C++ class instance when called from an embedded Python interpreter. None means load symbols from shared library. :type ptr: pointer :param comm: MPI communicator (as provided by `mpi4py `_). ``None`` means use ``MPI_COMM_WORLD`` implicitly. :type comm: MPI_Comm """ def __init__(self,name="",cmdargs=None,ptr=None,comm=None): super(IPyLammps, self).__init__(name=name,cmdargs=cmdargs,ptr=ptr,comm=comm) def image(self, filename="snapshot.png", group="all", color="type", diameter="type", size=None, view=None, center=None, up=None, zoom=1.0, background_color="white"): """ Generate image using write_dump command and display it See :doc:`dump image ` for more information. :param filename: Name of the image file that should be generated. The extension determines whether it is PNG or JPEG :type filename: string :param group: the group of atoms write_image should use :type group: string :param color: name of property used to determine color :type color: string :param diameter: name of property used to determine atom diameter :type diameter: string :param size: dimensions of image :type size: tuple (width, height) :param view: view parameters :type view: tuple (theta, phi) :param center: center parameters :type center: tuple (flag, center_x, center_y, center_z) :param up: vector pointing to up direction :type up: tuple (up_x, up_y, up_z) :param zoom: zoom factor :type zoom: float :param background_color: background color of scene :type background_color: string :return: Image instance used to display image in notebook :rtype: :py:class:`IPython.core.display.Image` """ cmd_args = [group, "image", filename, color, diameter] if size is not None: width = size[0] height = size[1] cmd_args += ["size", width, height] if view is not None: theta = view[0] phi = view[1] cmd_args += ["view", theta, phi] if center is not None: flag = center[0] Cx = center[1] Cy = center[2] Cz = center[3] cmd_args += ["center", flag, Cx, Cy, Cz] if up is not None: Ux = up[0] Uy = up[1] Uz = up[2] cmd_args += ["up", Ux, Uy, Uz] if zoom is not None: cmd_args += ["zoom", zoom] cmd_args.append("modify backcolor " + background_color) self.write_dump(*cmd_args) from IPython.core.display import Image return Image(filename) def video(self, filename): """ Load video from file Can be used to visualize videos from :doc:`dump movie `. :param filename: Path to video file :type filename: string :return: HTML Video Tag used by notebook to embed a video :rtype: :py:class:`IPython.display.HTML` """ from IPython.display import HTML return HTML("") # Local Variables: # fill-column: 100 # python-indent-offset: 2 # End: