Skip to content

vedo.applications.chemistry

chemistry

chemistry

Atom

A class representing an atom in a molecule, fully wrapping vtkAtom.

Provides access to all methods and properties of vtkAtom as documented in: https://vtk.org/doc/nightly/html/classvtkAtom.html

Source code in vedo/applications/chemistry.py
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
class Atom:
    """
    A class representing an atom in a molecule, fully wrapping vtkAtom.

    Provides access to all methods and properties of vtkAtom as documented in:
    https://vtk.org/doc/nightly/html/classvtkAtom.html
    """

    def __init__(self, molecule, atom_id):
        """
        Initialize the Atom with a reference to the molecule and its ID.

        Args:
            molecule (vtkMolecule):
                The molecule containing this atom.
            atom_id (int):
                The ID of the atom in the molecule.
        """
        self.molecule = molecule
        self.atom_id = atom_id
        self.vtk_atom = self.molecule.GetAtom(self.atom_id)

    def get_atomic_number(self):
        """Get the atomic number of the atom.

        Returns:
            The atomic number.
        """
        return self.vtk_atom.GetAtomicNumber()

    def set_atomic_number(self, atomic_number):
        """Set the atomic number of the atom.

        Args:
            atomic_number (int):
                The new atomic number.
        """
        self.vtk_atom.SetAtomicNumber(atomic_number)

    def get_position(self):
        """Get the position of the atom as a NumPy array.

        Returns:
            Array of shape (3,) with [x, y, z] coordinates.
        """
        pos = self.vtk_atom.GetPosition()
        return np.array([pos[0], pos[1], pos[2]])

    def set_position(self, position):
        """Set the position of the atom.

        Args:
            position (list or np.ndarray):
                The new [x, y, z] coordinates.
        """
        pos = np.asarray(position, dtype=float)
        self.vtk_atom.SetPosition(pos[0], pos[1], pos[2])

    def get_atom_id(self):
        """Get the ID of this atom.

        Returns:
            The atom's ID within the molecule.
        """
        return self.atom_id

    def get_molecule(self):
        """Get the molecule this atom belongs to.

        Returns:
            The parent molecule.
        """
        return self.molecule

    def __repr__(self):
        return (
            f"Atom(ID={self.atom_id}, "
            f"AtomicNumber={self.get_atomic_number()}, "
            f"Position={self.get_position().tolist()})"
        )

get_atom_id()

Get the ID of this atom.

Returns:

Type Description

The atom's ID within the molecule.

Source code in vedo/applications/chemistry.py
311
312
313
314
315
316
317
def get_atom_id(self):
    """Get the ID of this atom.

    Returns:
        The atom's ID within the molecule.
    """
    return self.atom_id

get_atomic_number()

Get the atomic number of the atom.

Returns:

Type Description

The atomic number.

Source code in vedo/applications/chemistry.py
275
276
277
278
279
280
281
def get_atomic_number(self):
    """Get the atomic number of the atom.

    Returns:
        The atomic number.
    """
    return self.vtk_atom.GetAtomicNumber()

get_molecule()

Get the molecule this atom belongs to.

Returns:

Type Description

The parent molecule.

Source code in vedo/applications/chemistry.py
319
320
321
322
323
324
325
def get_molecule(self):
    """Get the molecule this atom belongs to.

    Returns:
        The parent molecule.
    """
    return self.molecule

get_position()

Get the position of the atom as a NumPy array.

Returns:

Type Description

Array of shape (3,) with [x, y, z] coordinates.

Source code in vedo/applications/chemistry.py
292
293
294
295
296
297
298
299
def get_position(self):
    """Get the position of the atom as a NumPy array.

    Returns:
        Array of shape (3,) with [x, y, z] coordinates.
    """
    pos = self.vtk_atom.GetPosition()
    return np.array([pos[0], pos[1], pos[2]])

set_atomic_number(atomic_number)

Set the atomic number of the atom.

Parameters:

Name Type Description Default
atomic_number int

The new atomic number.

required
Source code in vedo/applications/chemistry.py
283
284
285
286
287
288
289
290
def set_atomic_number(self, atomic_number):
    """Set the atomic number of the atom.

    Args:
        atomic_number (int):
            The new atomic number.
    """
    self.vtk_atom.SetAtomicNumber(atomic_number)

set_position(position)

Set the position of the atom.

Parameters:

Name Type Description Default
position list or ndarray

The new [x, y, z] coordinates.

required
Source code in vedo/applications/chemistry.py
301
302
303
304
305
306
307
308
309
def set_position(self, position):
    """Set the position of the atom.

    Args:
        position (list or np.ndarray):
            The new [x, y, z] coordinates.
    """
    pos = np.asarray(position, dtype=float)
    self.vtk_atom.SetPosition(pos[0], pos[1], pos[2])

Molecule

Source code in vedo/applications/chemistry.py
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
class Molecule:
    def __init__(self, input_data=None):
        # Create an empty molecule
        self.molecule = vtki.new("Molecule")

        # Configure the mapper and actor for rendering
        self.mapper = vtki.new("MoleculeMapper")
        self.actor = vtki.new("Actor")
        self.property = self.actor.GetProperty()

        if isinstance(input_data, str):
            if input_data.startswith("https://"):
                input_data = vedo.file_io.download(input_data, verbose=False)
            # Create and configure the PDB reader
            reader = vtki.new("PDBReader")
            reader.SetFileName(input_data)
            reader.Update()
            pdb_data = reader.GetOutput()
        elif isinstance(input_data, vtki.vtkMolecule):
            self.molecule = input_data
            pdb_data = None
        elif isinstance(input_data, vtki.vtkPolyData):
            pdb_data = input_data
        elif hasattr(input_data, "dataset") and isinstance(input_data.dataset, vtki.vtkPolyData):
            pdb_data = input_data.dataset
        else:
            pdb_data = None

        if pdb_data is not None:
            points = pdb_data.GetPoints()
            if points is None:
                vedo.logger.error(f"Could not read molecule input {input_data}")
            else:
                point_data = pdb_data.GetPointData()

                # Extract atom information and add to molecule
                for i in range(points.GetNumberOfPoints()):
                    position = points.GetPoint(i)
                    # Default to Carbon if atomic number is not available
                    atomic_number = 6
                    if point_data.GetScalars("atom_type"):
                        atomic_number = int(point_data.GetScalars("atom_type").GetValue(i))
                    # Add atom to molecule
                    self.molecule.AppendAtom(atomic_number, position)

                # Add bonds if available
                if pdb_data.GetLines():
                    lines = pdb_data.GetLines()
                    lines.InitTraversal()
                    id_list = vtki.new("IdList")
                    while lines.GetNextCell(id_list):
                        if id_list.GetNumberOfIds() == 2:
                            self.molecule.AppendBond(
                                id_list.GetId(0),
                                id_list.GetId(1),
                                1,  # Default to single bond
                            )

        # Set the molecule as input to the mapper
        self.mapper.SetInputData(self.molecule)
        self.actor.SetMapper(self.mapper)

        # Apply default rendering style
        self.use_ball_and_stick()

    def append_atom(self, position=None, atomic_number=6):
        """Add an atom to the molecule with optional position and atomic number.

        Args:
            position (list or np.ndarray):
                [x, y, z] coordinates. Defaults to [0, 0, 0].
            atomic_number (int):
                Atomic number (e.g., 6 for Carbon). Defaults to 6.

        Returns:
            The added atom object.
        """
        if position is None:
            position = [0, 0, 0]
        pos = np.asarray(position, dtype=float)
        self.molecule.AppendAtom(atomic_number, pos[0], pos[1], pos[2])
        atom_id = (
            self.molecule.GetNumberOfAtoms() - 1
        )  # The ID will be the index of the last added atom
        return Atom(self.molecule, atom_id)

    def get_atom(self, atom_id):
        """Retrieve an atom by its ID.

        Args:
            atom_id (int):
                The ID of the atom.

        Returns:
            The atom object.
        """
        if atom_id < 0 or atom_id >= self.get_number_of_atoms():
            raise ValueError(f"Atom ID {atom_id} exceeds number of atoms.")
        return Atom(self.molecule, atom_id)

    def remove_atom(self, atom_id):
        """Remove an atom by its ID.

        Args:
            atom_id (int): The ID of the atom to remove.
        """
        if atom_id < 0 or atom_id >= self.get_number_of_atoms():
            raise ValueError(f"Atom ID {atom_id} exceeds number of atoms.")
        self.molecule.RemoveAtom(atom_id)
        # Update the mapper to reflect the changes
        self.mapper.SetInputData(self.molecule)
        self.mapper.Update()
        # Update the actor to reflect the changes
        self.actor.SetMapper(self.mapper)
        self.actor.Update()
        # Keep user-defined visual properties unchanged after topology edits.

    def get_array(self, name):
        """Get a point data array by name.

        The following arrays are available:

        - atom_type: Atomic number of the atom.
        - atom_types: Atomic type of the atom.
        - residue: Residue name.
        - chain: Chain identifier.
        - secondary_structures: Secondary structure type.
        - secondary_structures_begin: Start index of the secondary structure.
        - secondary_structures_end: End index of the secondary structure.
        - ishetatm: Is the atom a heteroatom?
        - model: Model number.
        - rgb_colors: RGB color of the atom.
        - radius: Radius of the atom.

        Args:
            name (str):
                The name of the array.

        Returns:
            The array data.
        """
        if not self.molecule.GetPointData().HasArray(name):
            raise ValueError(f"Array '{name}' not found in molecule.")
        return vedo.utils.vtk2numpy(self.molecule.GetPointData().GetArray(name))

    def append_bond(self, atom1, atom2, order=1):
        """Add a bond between two atoms.

        Args:
            atom1 (Atom or int):
                The first atom or its ID.
            atom2 (Atom or int):
                The second atom or its ID.
            order (int):
                Bond order (1=single, 2=double, 3=triple).
        """
        atom1_id = atom1.atom_id if isinstance(atom1, Atom) else atom1
        atom2_id = atom2.atom_id if isinstance(atom2, Atom) else atom2
        if not isinstance(atom1_id, int) or not isinstance(atom2_id, int):
            raise TypeError("append_bond() atom references must be Atom or int IDs.")
        n_atoms = self.get_number_of_atoms()
        if atom1_id < 0 or atom1_id >= n_atoms or atom2_id < 0 or atom2_id >= n_atoms:
            raise ValueError(f"Atom IDs must be in [0, {n_atoms - 1}]")
        if int(order) < 1:
            raise ValueError("Bond order must be >= 1.")
        self.molecule.AppendBond(atom1_id, atom2_id, order)

    def get_number_of_atoms(self):
        """Get the number of atoms in the molecule.

        Returns:
            Number of atoms.
        """
        return self.molecule.GetNumberOfAtoms()

    def get_number_of_bonds(self):
        """Get the number of bonds in the molecule.

        Returns:
            Number of bonds.
        """
        return self.molecule.GetNumberOfBonds()

    def get_bond(self, bond_id):
        """Get bond information by ID (simplified as VTK bond access is limited).

        Args:
            bond_id (int):
                The ID of the bond.

        Returns:
            (atom1_id, atom2_id, order).
        """
        if bond_id < 0 or bond_id >= self.get_number_of_bonds():
            raise ValueError(f"Bond ID {bond_id} exceeds number of bonds.")
        bond = self.molecule.GetBond(bond_id)
        return (bond.GetBeginAtomId(), bond.GetEndAtomId(), bond.GetOrder())

    def get_atom_positions(self):
        """Get the positions of all atoms.

        Returns:
            Array of shape (n_atoms, 3) with [x, y, z] coordinates.
        """
        n_atoms = self.get_number_of_atoms()
        positions = np.zeros((n_atoms, 3))
        for i in range(n_atoms):
            pos = self.molecule.GetAtom(i).GetPosition()
            positions[i] = [pos[0], pos[1], pos[2]]
        return positions

    def set_atom_positions(self, positions):
        """
        Set the positions of all atoms.

        Args:
            positions (np.ndarray):
                Array of shape (n_atoms, 3) with [x, y, z] coordinates.
        """
        n_atoms = self.get_number_of_atoms()
        positions = np.asarray(positions, dtype=float)
        if positions.shape != (n_atoms, 3):
            raise ValueError(
                f"Expected positions shape ({n_atoms}, 3), got {positions.shape}"
            )
        for i in range(n_atoms):
            self.molecule.GetAtom(i).SetPosition(positions[i])

    def get_atomic_numbers(self):
        """
        Get the atomic numbers of all atoms.

        Returns:
            List of atomic numbers.
        """
        return [
            self.molecule.GetAtom(i).GetAtomicNumber()
            for i in range(self.get_number_of_atoms())
        ]

    def set_atomic_numbers(self, atomic_numbers):
        """
        Set the atomic numbers of all atoms.

        Args:
            atomic_numbers (list):
                List of atomic numbers.
        """
        n_atoms = self.get_number_of_atoms()
        if len(atomic_numbers) != n_atoms:
            raise ValueError(
                f"Expected {n_atoms} atomic numbers, got {len(atomic_numbers)}"
            )
        for i, num in enumerate(atomic_numbers):
            self.molecule.GetAtom(i).SetAtomicNumber(num)

    # Rendering customization methods
    def use_ball_and_stick(self):
        """Set the molecule to use ball-and-stick representation."""
        self.mapper.UseBallAndStickSettings()
        return self

    def use_space_filling(self):
        """Set the molecule to use space-filling (VDW spheres) representation."""
        self.mapper.UseVDWSpheresSettings()
        return self

    def set_atom_radius_scale(self, scale):
        """Set the scale factor for atom radii.

        Args:
            scale (float):
                Scaling factor for atom spheres.
        """
        self.mapper.SetAtomicRadiusScaleFactor(scale)
        return self

    def set_bond_radius(self, radius):
        """Set the radius of bonds.

        Args:
            radius (float):
                Bond radius in world units.
        """
        self.mapper.SetBondRadius(radius)
        return self

    def use_multi_cylinders_for_bonds(self, value=True):
        """Render each bond as one cylinder or as multi-colored half-cylinders."""
        self.mapper.SetUseMultiCylindersForBonds(value)
        return self

    def set_bond_color_mode(self, mode="discrete"):
        """Set bond coloring mode to either 'discrete' or 'single'."""
        if str(mode).lower().startswith("single"):
            self.mapper.SetBondColorModeToSingleColor()
        else:
            self.mapper.SetBondColorModeToDiscreteByAtom()
        return self

    def set_bond_color(self, color):
        """Set a single bond color."""
        rgb = (np.array(get_color(color)) * 255).astype(np.uint8)
        self.mapper.SetBondColor(int(rgb[0]), int(rgb[1]), int(rgb[2]))
        return self

append_atom(position=None, atomic_number=6)

Add an atom to the molecule with optional position and atomic number.

Parameters:

Name Type Description Default
position list or ndarray

[x, y, z] coordinates. Defaults to [0, 0, 0].

None
atomic_number int

Atomic number (e.g., 6 for Carbon). Defaults to 6.

6

Returns:

Type Description

The added atom object.

Source code in vedo/applications/chemistry.py
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
def append_atom(self, position=None, atomic_number=6):
    """Add an atom to the molecule with optional position and atomic number.

    Args:
        position (list or np.ndarray):
            [x, y, z] coordinates. Defaults to [0, 0, 0].
        atomic_number (int):
            Atomic number (e.g., 6 for Carbon). Defaults to 6.

    Returns:
        The added atom object.
    """
    if position is None:
        position = [0, 0, 0]
    pos = np.asarray(position, dtype=float)
    self.molecule.AppendAtom(atomic_number, pos[0], pos[1], pos[2])
    atom_id = (
        self.molecule.GetNumberOfAtoms() - 1
    )  # The ID will be the index of the last added atom
    return Atom(self.molecule, atom_id)

append_bond(atom1, atom2, order=1)

Add a bond between two atoms.

Parameters:

Name Type Description Default
atom1 Atom or int

The first atom or its ID.

required
atom2 Atom or int

The second atom or its ID.

required
order int

Bond order (1=single, 2=double, 3=triple).

1
Source code in vedo/applications/chemistry.py
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
def append_bond(self, atom1, atom2, order=1):
    """Add a bond between two atoms.

    Args:
        atom1 (Atom or int):
            The first atom or its ID.
        atom2 (Atom or int):
            The second atom or its ID.
        order (int):
            Bond order (1=single, 2=double, 3=triple).
    """
    atom1_id = atom1.atom_id if isinstance(atom1, Atom) else atom1
    atom2_id = atom2.atom_id if isinstance(atom2, Atom) else atom2
    if not isinstance(atom1_id, int) or not isinstance(atom2_id, int):
        raise TypeError("append_bond() atom references must be Atom or int IDs.")
    n_atoms = self.get_number_of_atoms()
    if atom1_id < 0 or atom1_id >= n_atoms or atom2_id < 0 or atom2_id >= n_atoms:
        raise ValueError(f"Atom IDs must be in [0, {n_atoms - 1}]")
    if int(order) < 1:
        raise ValueError("Bond order must be >= 1.")
    self.molecule.AppendBond(atom1_id, atom2_id, order)

get_array(name)

Get a point data array by name.

The following arrays are available:

  • atom_type: Atomic number of the atom.
  • atom_types: Atomic type of the atom.
  • residue: Residue name.
  • chain: Chain identifier.
  • secondary_structures: Secondary structure type.
  • secondary_structures_begin: Start index of the secondary structure.
  • secondary_structures_end: End index of the secondary structure.
  • ishetatm: Is the atom a heteroatom?
  • model: Model number.
  • rgb_colors: RGB color of the atom.
  • radius: Radius of the atom.

Parameters:

Name Type Description Default
name str

The name of the array.

required

Returns:

Type Description

The array data.

Source code in vedo/applications/chemistry.py
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
def get_array(self, name):
    """Get a point data array by name.

    The following arrays are available:

    - atom_type: Atomic number of the atom.
    - atom_types: Atomic type of the atom.
    - residue: Residue name.
    - chain: Chain identifier.
    - secondary_structures: Secondary structure type.
    - secondary_structures_begin: Start index of the secondary structure.
    - secondary_structures_end: End index of the secondary structure.
    - ishetatm: Is the atom a heteroatom?
    - model: Model number.
    - rgb_colors: RGB color of the atom.
    - radius: Radius of the atom.

    Args:
        name (str):
            The name of the array.

    Returns:
        The array data.
    """
    if not self.molecule.GetPointData().HasArray(name):
        raise ValueError(f"Array '{name}' not found in molecule.")
    return vedo.utils.vtk2numpy(self.molecule.GetPointData().GetArray(name))

get_atom(atom_id)

Retrieve an atom by its ID.

Parameters:

Name Type Description Default
atom_id int

The ID of the atom.

required

Returns:

Type Description

The atom object.

Source code in vedo/applications/chemistry.py
421
422
423
424
425
426
427
428
429
430
431
432
433
def get_atom(self, atom_id):
    """Retrieve an atom by its ID.

    Args:
        atom_id (int):
            The ID of the atom.

    Returns:
        The atom object.
    """
    if atom_id < 0 or atom_id >= self.get_number_of_atoms():
        raise ValueError(f"Atom ID {atom_id} exceeds number of atoms.")
    return Atom(self.molecule, atom_id)

get_atom_positions()

Get the positions of all atoms.

Returns:

Type Description

Array of shape (n_atoms, 3) with [x, y, z] coordinates.

Source code in vedo/applications/chemistry.py
533
534
535
536
537
538
539
540
541
542
543
544
def get_atom_positions(self):
    """Get the positions of all atoms.

    Returns:
        Array of shape (n_atoms, 3) with [x, y, z] coordinates.
    """
    n_atoms = self.get_number_of_atoms()
    positions = np.zeros((n_atoms, 3))
    for i in range(n_atoms):
        pos = self.molecule.GetAtom(i).GetPosition()
        positions[i] = [pos[0], pos[1], pos[2]]
    return positions

get_atomic_numbers()

Get the atomic numbers of all atoms.

Returns:

Type Description

List of atomic numbers.

Source code in vedo/applications/chemistry.py
563
564
565
566
567
568
569
570
571
572
573
def get_atomic_numbers(self):
    """
    Get the atomic numbers of all atoms.

    Returns:
        List of atomic numbers.
    """
    return [
        self.molecule.GetAtom(i).GetAtomicNumber()
        for i in range(self.get_number_of_atoms())
    ]

get_bond(bond_id)

Get bond information by ID (simplified as VTK bond access is limited).

Parameters:

Name Type Description Default
bond_id int

The ID of the bond.

required

Returns:

Type Description

(atom1_id, atom2_id, order).

Source code in vedo/applications/chemistry.py
518
519
520
521
522
523
524
525
526
527
528
529
530
531
def get_bond(self, bond_id):
    """Get bond information by ID (simplified as VTK bond access is limited).

    Args:
        bond_id (int):
            The ID of the bond.

    Returns:
        (atom1_id, atom2_id, order).
    """
    if bond_id < 0 or bond_id >= self.get_number_of_bonds():
        raise ValueError(f"Bond ID {bond_id} exceeds number of bonds.")
    bond = self.molecule.GetBond(bond_id)
    return (bond.GetBeginAtomId(), bond.GetEndAtomId(), bond.GetOrder())

get_number_of_atoms()

Get the number of atoms in the molecule.

Returns:

Type Description

Number of atoms.

Source code in vedo/applications/chemistry.py
502
503
504
505
506
507
508
def get_number_of_atoms(self):
    """Get the number of atoms in the molecule.

    Returns:
        Number of atoms.
    """
    return self.molecule.GetNumberOfAtoms()

get_number_of_bonds()

Get the number of bonds in the molecule.

Returns:

Type Description

Number of bonds.

Source code in vedo/applications/chemistry.py
510
511
512
513
514
515
516
def get_number_of_bonds(self):
    """Get the number of bonds in the molecule.

    Returns:
        Number of bonds.
    """
    return self.molecule.GetNumberOfBonds()

remove_atom(atom_id)

Remove an atom by its ID.

Parameters:

Name Type Description Default
atom_id int

The ID of the atom to remove.

required
Source code in vedo/applications/chemistry.py
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
def remove_atom(self, atom_id):
    """Remove an atom by its ID.

    Args:
        atom_id (int): The ID of the atom to remove.
    """
    if atom_id < 0 or atom_id >= self.get_number_of_atoms():
        raise ValueError(f"Atom ID {atom_id} exceeds number of atoms.")
    self.molecule.RemoveAtom(atom_id)
    # Update the mapper to reflect the changes
    self.mapper.SetInputData(self.molecule)
    self.mapper.Update()
    # Update the actor to reflect the changes
    self.actor.SetMapper(self.mapper)
    self.actor.Update()

set_atom_positions(positions)

Set the positions of all atoms.

Parameters:

Name Type Description Default
positions ndarray

Array of shape (n_atoms, 3) with [x, y, z] coordinates.

required
Source code in vedo/applications/chemistry.py
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
def set_atom_positions(self, positions):
    """
    Set the positions of all atoms.

    Args:
        positions (np.ndarray):
            Array of shape (n_atoms, 3) with [x, y, z] coordinates.
    """
    n_atoms = self.get_number_of_atoms()
    positions = np.asarray(positions, dtype=float)
    if positions.shape != (n_atoms, 3):
        raise ValueError(
            f"Expected positions shape ({n_atoms}, 3), got {positions.shape}"
        )
    for i in range(n_atoms):
        self.molecule.GetAtom(i).SetPosition(positions[i])

set_atom_radius_scale(scale)

Set the scale factor for atom radii.

Parameters:

Name Type Description Default
scale float

Scaling factor for atom spheres.

required
Source code in vedo/applications/chemistry.py
602
603
604
605
606
607
608
609
610
def set_atom_radius_scale(self, scale):
    """Set the scale factor for atom radii.

    Args:
        scale (float):
            Scaling factor for atom spheres.
    """
    self.mapper.SetAtomicRadiusScaleFactor(scale)
    return self

set_atomic_numbers(atomic_numbers)

Set the atomic numbers of all atoms.

Parameters:

Name Type Description Default
atomic_numbers list

List of atomic numbers.

required
Source code in vedo/applications/chemistry.py
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
def set_atomic_numbers(self, atomic_numbers):
    """
    Set the atomic numbers of all atoms.

    Args:
        atomic_numbers (list):
            List of atomic numbers.
    """
    n_atoms = self.get_number_of_atoms()
    if len(atomic_numbers) != n_atoms:
        raise ValueError(
            f"Expected {n_atoms} atomic numbers, got {len(atomic_numbers)}"
        )
    for i, num in enumerate(atomic_numbers):
        self.molecule.GetAtom(i).SetAtomicNumber(num)

set_bond_color(color)

Set a single bond color.

Source code in vedo/applications/chemistry.py
635
636
637
638
639
def set_bond_color(self, color):
    """Set a single bond color."""
    rgb = (np.array(get_color(color)) * 255).astype(np.uint8)
    self.mapper.SetBondColor(int(rgb[0]), int(rgb[1]), int(rgb[2]))
    return self

set_bond_color_mode(mode='discrete')

Set bond coloring mode to either 'discrete' or 'single'.

Source code in vedo/applications/chemistry.py
627
628
629
630
631
632
633
def set_bond_color_mode(self, mode="discrete"):
    """Set bond coloring mode to either 'discrete' or 'single'."""
    if str(mode).lower().startswith("single"):
        self.mapper.SetBondColorModeToSingleColor()
    else:
        self.mapper.SetBondColorModeToDiscreteByAtom()
    return self

set_bond_radius(radius)

Set the radius of bonds.

Parameters:

Name Type Description Default
radius float

Bond radius in world units.

required
Source code in vedo/applications/chemistry.py
612
613
614
615
616
617
618
619
620
def set_bond_radius(self, radius):
    """Set the radius of bonds.

    Args:
        radius (float):
            Bond radius in world units.
    """
    self.mapper.SetBondRadius(radius)
    return self

use_ball_and_stick()

Set the molecule to use ball-and-stick representation.

Source code in vedo/applications/chemistry.py
592
593
594
595
def use_ball_and_stick(self):
    """Set the molecule to use ball-and-stick representation."""
    self.mapper.UseBallAndStickSettings()
    return self

use_multi_cylinders_for_bonds(value=True)

Render each bond as one cylinder or as multi-colored half-cylinders.

Source code in vedo/applications/chemistry.py
622
623
624
625
def use_multi_cylinders_for_bonds(self, value=True):
    """Render each bond as one cylinder or as multi-colored half-cylinders."""
    self.mapper.SetUseMultiCylindersForBonds(value)
    return self

use_space_filling()

Set the molecule to use space-filling (VDW spheres) representation.

Source code in vedo/applications/chemistry.py
597
598
599
600
def use_space_filling(self):
    """Set the molecule to use space-filling (VDW spheres) representation."""
    self.mapper.UseVDWSpheresSettings()
    return self

PeriodicTable

A Vedo-compatible class for accessing periodic table data, wrapping vtkPeriodicTable.

This class provides access to element properties such as atomic numbers, names, symbols, and covalent radii, using VTK's built-in periodic table database.

Attributes:

Name Type Description
periodic_table vtkPeriodicTable

The underlying VTK periodic table object.

Source code in vedo/applications/chemistry.py
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
class PeriodicTable:
    """
    A Vedo-compatible class for accessing periodic table data, wrapping vtkPeriodicTable.

    This class provides access to element properties such as atomic numbers, names,
    symbols, and covalent radii, using VTK's built-in periodic table database.

    Attributes:
        periodic_table (vtkPeriodicTable): The underlying VTK periodic table object.
    """

    def __init__(self):
        """
        Initialize the PeriodicTable with VTK's built-in periodic table data.
        """
        self.periodic_table = vtki.new("PeriodicTable")

    def get_element_name(self, atomic_number):
        """
        Get the name of the element with the given atomic number.

        Args:
            atomic_number (int):
                The atomic number of the element.

        Returns:
            str: The name of the element.
        """
        return self.periodic_table.GetElementName(atomic_number)

    def get_element_symbol(self, atomic_number):
        """
        Get the symbol of the element with the given atomic number.

        Args:
            atomic_number (int):
                The atomic number of the element.

        Returns:
            The symbol of the element.
        """
        return self.periodic_table.GetSymbol(atomic_number)

    def get_atomic_number(self, symbol):
        """
        Get the atomic number of the element with the given symbol.

        Args:
            symbol (str):
                The symbol of the element.

        Returns:
            The atomic number of the element.
        """
        return self.periodic_table.GetAtomicNumber(symbol)

    def get_covalent_radius(self, atomic_number):
        """
        Get the covalent radius of the element with the given atomic number.

        Args:
            atomic_number (int):
                The atomic number of the element.

        Returns:
            The covalent radius of the element.
        """
        return self.periodic_table.GetCovalentRadius(atomic_number)

    def get_vdw_radius(self, atomic_number):
        """
        Get the Van der Waals radius of the element with the given atomic number.

        Args:
            atomic_number (int):
                The atomic number of the element.

        Returns:
            The Van der Waals radius of the element.
        """
        return self.periodic_table.GetVDWRadius(atomic_number)

    def get_number_of_elements(self):
        """
        Get the total number of elements in the periodic table.

        Returns:
            The number of elements.
        """
        return self.periodic_table.GetNumberOfElements()

    def get_element_data(self, atomic_number):
        """
        Get all data for the element with the given atomic number.

        Args:
            atomic_number (int):
                The atomic number of the element.

        Returns:
            A dictionary containing the element's name, symbol, and radii.
        """
        return {
            "name": self.get_element_name(atomic_number),
            "symbol": self.get_element_symbol(atomic_number),
            "covalent_radius": self.get_covalent_radius(atomic_number),
            "vdw_radius": self.get_vdw_radius(atomic_number),
        }

    def __getitem__(self, atomic_number):
        """
        Get element data by atomic number.

        Args:
            atomic_number (int):
                The atomic number of the element.

        Returns:
            A dictionary containing the element's name, symbol, and radii.
        """
        return self.get_element_data(atomic_number)

    def __len__(self):
        """
        Get the number of elements in the periodic table.

        Returns:
            The number of elements.
        """
        return self.get_number_of_elements()

    def __iter__(self):
        """
        Iterate over all elements in the periodic table.

        Yields:
            tuple: (atomic_number, element_data) for each element.
        """
        for atomic_number in range(1, self.get_number_of_elements() + 1):
            yield atomic_number, self.get_element_data(atomic_number)

    def __contains__(self, atomic_number):
        """
        Check if an atomic number is in the periodic table.

        Args:
            atomic_number (int):
                The atomic number to check.

        Returns:
            bool: True if the atomic number exists, False otherwise.
        """
        return 1 <= atomic_number <= self.get_number_of_elements()

    def __str__(self):
        return summary_string(self, self._summary_rows())

    def __repr__(self):
        return self.__str__()

    def __rich__(self):
        return summary_panel(self, self._summary_rows())

    def _summary_rows(self):
        n = self.get_number_of_elements()
        rows = [("Number of elements", str(n))]
        # Example usage of the periodic table
        atomic_number = 12
        rows.append(("Atomic number", f"{atomic_number} (example entry)"))
        rows.append(("Element name", f"{self.get_element_name(atomic_number)}"))
        rows.append(("Element symbol", f"{self.get_element_symbol(atomic_number)}"))
        rows.append(("Covalent radius", f"{self.get_covalent_radius(atomic_number)}"))
        rows.append(("Van der Waals radius", f"{self.get_vdw_radius(atomic_number)}"))
        return rows

get_atomic_number(symbol)

Get the atomic number of the element with the given symbol.

Parameters:

Name Type Description Default
symbol str

The symbol of the element.

required

Returns:

Type Description

The atomic number of the element.

Source code in vedo/applications/chemistry.py
61
62
63
64
65
66
67
68
69
70
71
72
def get_atomic_number(self, symbol):
    """
    Get the atomic number of the element with the given symbol.

    Args:
        symbol (str):
            The symbol of the element.

    Returns:
        The atomic number of the element.
    """
    return self.periodic_table.GetAtomicNumber(symbol)

get_covalent_radius(atomic_number)

Get the covalent radius of the element with the given atomic number.

Parameters:

Name Type Description Default
atomic_number int

The atomic number of the element.

required

Returns:

Type Description

The covalent radius of the element.

Source code in vedo/applications/chemistry.py
74
75
76
77
78
79
80
81
82
83
84
85
def get_covalent_radius(self, atomic_number):
    """
    Get the covalent radius of the element with the given atomic number.

    Args:
        atomic_number (int):
            The atomic number of the element.

    Returns:
        The covalent radius of the element.
    """
    return self.periodic_table.GetCovalentRadius(atomic_number)

get_element_data(atomic_number)

Get all data for the element with the given atomic number.

Parameters:

Name Type Description Default
atomic_number int

The atomic number of the element.

required

Returns:

Type Description

A dictionary containing the element's name, symbol, and radii.

Source code in vedo/applications/chemistry.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def get_element_data(self, atomic_number):
    """
    Get all data for the element with the given atomic number.

    Args:
        atomic_number (int):
            The atomic number of the element.

    Returns:
        A dictionary containing the element's name, symbol, and radii.
    """
    return {
        "name": self.get_element_name(atomic_number),
        "symbol": self.get_element_symbol(atomic_number),
        "covalent_radius": self.get_covalent_radius(atomic_number),
        "vdw_radius": self.get_vdw_radius(atomic_number),
    }

get_element_name(atomic_number)

Get the name of the element with the given atomic number.

Parameters:

Name Type Description Default
atomic_number int

The atomic number of the element.

required

Returns:

Name Type Description
str

The name of the element.

Source code in vedo/applications/chemistry.py
35
36
37
38
39
40
41
42
43
44
45
46
def get_element_name(self, atomic_number):
    """
    Get the name of the element with the given atomic number.

    Args:
        atomic_number (int):
            The atomic number of the element.

    Returns:
        str: The name of the element.
    """
    return self.periodic_table.GetElementName(atomic_number)

get_element_symbol(atomic_number)

Get the symbol of the element with the given atomic number.

Parameters:

Name Type Description Default
atomic_number int

The atomic number of the element.

required

Returns:

Type Description

The symbol of the element.

Source code in vedo/applications/chemistry.py
48
49
50
51
52
53
54
55
56
57
58
59
def get_element_symbol(self, atomic_number):
    """
    Get the symbol of the element with the given atomic number.

    Args:
        atomic_number (int):
            The atomic number of the element.

    Returns:
        The symbol of the element.
    """
    return self.periodic_table.GetSymbol(atomic_number)

get_number_of_elements()

Get the total number of elements in the periodic table.

Returns:

Type Description

The number of elements.

Source code in vedo/applications/chemistry.py
100
101
102
103
104
105
106
107
def get_number_of_elements(self):
    """
    Get the total number of elements in the periodic table.

    Returns:
        The number of elements.
    """
    return self.periodic_table.GetNumberOfElements()

get_vdw_radius(atomic_number)

Get the Van der Waals radius of the element with the given atomic number.

Parameters:

Name Type Description Default
atomic_number int

The atomic number of the element.

required

Returns:

Type Description

The Van der Waals radius of the element.

Source code in vedo/applications/chemistry.py
87
88
89
90
91
92
93
94
95
96
97
98
def get_vdw_radius(self, atomic_number):
    """
    Get the Van der Waals radius of the element with the given atomic number.

    Args:
        atomic_number (int):
            The atomic number of the element.

    Returns:
        The Van der Waals radius of the element.
    """
    return self.periodic_table.GetVDWRadius(atomic_number)

Protein

A Vedo-compatible class for protein ribbon visualization, wrapping vtkProteinRibbonFilter.

This class generates a ribbon representation of protein structures from PDB files, vtkMolecule objects, or vtkPolyData, and integrates with Vedo's rendering system.

Attributes:

Name Type Description
filter vtkProteinRibbonFilter

The underlying VTK filter for ribbon generation.

mapper vtkPolyDataMapper

Maps the filter's output to renderable data.

actor vtkActor

The VTK actor for rendering the ribbon.

Source code in vedo/applications/chemistry.py
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
class Protein:
    """
    A Vedo-compatible class for protein ribbon visualization, wrapping vtkProteinRibbonFilter.

    This class generates a ribbon representation of protein structures from PDB files,
    vtkMolecule objects, or vtkPolyData, and integrates with Vedo's rendering system.

    Attributes:
        filter (vtkProteinRibbonFilter): The underlying VTK filter for ribbon generation.
        mapper (vtkPolyDataMapper): Maps the filter's output to renderable data.
        actor (vtkActor): The VTK actor for rendering the ribbon.
    """

    def __init__(self, input_data):
        """
        Initialize the ProteinRibbon with input data.

        Args:
            input_data (str or vtkMolecule or vtkPolyData):
                - Path to a PDB file (str)
                - A vtkMolecule object
                - A vtkPolyData object

        Raises:
            ValueError: If the input_data type is not supported.
        """

        # Handle different input types
        if isinstance(input_data, str):
            if input_data.startswith("https://"):
                input_data = vedo.file_io.download(input_data, verbose=False)
            # Read PDB file using vtkPDBReader
            reader = vtki.new("PDBReader")
            reader.SetFileName(input_data)
            reader.Update()
            self.input_data = reader.GetOutput()
        elif isinstance(input_data, vtki.vtkMolecule):
            self.input_data = input_data
        elif isinstance(input_data, vtki.vtkPolyData):
            self.input_data = input_data
        else:
            raise ValueError(
                "Input must be a PDB file path, vtkMolecule, or vtkPolyData."
            )

        if (
            self.input_data is None
            or not self.input_data.GetPoints()
            or self.input_data.GetNumberOfPoints() == 0
        ):
            vedo.logger.error(f"Could not read protein input {input_data}")

        # Create and configure the ribbon filter
        self.filter = vtki.new("ProteinRibbonFilter")
        self.filter.SetInputData(self.input_data)

        # Set up the mapper and actor for rendering
        self.mapper = vtki.new("PolyDataMapper")
        self.mapper.SetInputConnection(self.filter.GetOutputPort())
        self.actor = vtki.new("Actor")
        self.actor.SetMapper(self.mapper)
        self.property = self.actor.GetProperty()
        self._refresh_mapper_input()

        # Keep the ribbon coloring from vtkProteinRibbonFilter, but use a darker palette
        # so proteins remain visible on vedo's white background.
        self.property.SetColor(1.0, 1.0, 1.0)
        self.property.SetOpacity(1.0)

    def _refresh_mapper_input(self):
        self.filter.Update()
        rgb_array = self.filter.GetOutput().GetPointData().GetArray("RGB")
        if rgb_array:
            rgb = vedo.utils.vtk2numpy(rgb_array)
            rgb[:] = np.clip(rgb.astype(np.float32) * 0.3, 0, 255).astype(np.uint8)
            rgb_array.Modified()
        self.mapper.Update()
        return self

    def set_coil_width(self, width):
        """
        Set the width of the coil regions in the ribbon.

        Args:
            width (float):
                The width of the coil regions.
        """
        self.filter.SetCoilWidth(width)
        return self._refresh_mapper_input()

    def set_helix_width(self, width):
        """
        Set the width of the helix regions in the ribbon.

        Args:
            width (float):
                The width of the helix regions.
        """
        self.filter.SetHelixWidth(width)
        return self._refresh_mapper_input()

    def set_sphere_resolution(self, resolution):
        """
        Set the resolution of spheres used in the ribbon representation.

        Args:
            resolution (int):
                The resolution of the spheres.
        """
        self.filter.SetSphereResolution(resolution)
        return self._refresh_mapper_input()

set_coil_width(width)

Set the width of the coil regions in the ribbon.

Parameters:

Name Type Description Default
width float

The width of the coil regions.

required
Source code in vedo/applications/chemistry.py
721
722
723
724
725
726
727
728
729
730
def set_coil_width(self, width):
    """
    Set the width of the coil regions in the ribbon.

    Args:
        width (float):
            The width of the coil regions.
    """
    self.filter.SetCoilWidth(width)
    return self._refresh_mapper_input()

set_helix_width(width)

Set the width of the helix regions in the ribbon.

Parameters:

Name Type Description Default
width float

The width of the helix regions.

required
Source code in vedo/applications/chemistry.py
732
733
734
735
736
737
738
739
740
741
def set_helix_width(self, width):
    """
    Set the width of the helix regions in the ribbon.

    Args:
        width (float):
            The width of the helix regions.
    """
    self.filter.SetHelixWidth(width)
    return self._refresh_mapper_input()

set_sphere_resolution(resolution)

Set the resolution of spheres used in the ribbon representation.

Parameters:

Name Type Description Default
resolution int

The resolution of the spheres.

required
Source code in vedo/applications/chemistry.py
743
744
745
746
747
748
749
750
751
752
def set_sphere_resolution(self, resolution):
    """
    Set the resolution of spheres used in the ribbon representation.

    Args:
        resolution (int):
            The resolution of the spheres.
    """
    self.filter.SetSphereResolution(resolution)
    return self._refresh_mapper_input()

append_molecules(molecules)

Append multiple molecules into a single molecule.

This function takes a list of Molecule objects and returns a new Molecule object that combines all atoms and bonds from the input molecules.

Parameters:

Name Type Description Default
molecules list of Molecule

The molecules to append.

required

Returns:

Name Type Description
Molecule

A new Molecule object containing all atoms and bonds from the input molecules.

Source code in vedo/applications/chemistry.py
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
def append_molecules(molecules):
    """
    Append multiple molecules into a single molecule.

    This function takes a list of Molecule objects and returns a new Molecule
    object that combines all atoms and bonds from the input molecules.

    Args:
        molecules (list of Molecule):
            The molecules to append.

    Returns:
        Molecule: A new Molecule object containing all atoms and bonds from the input molecules.
    """
    if not molecules:
        raise ValueError("No molecules provided to append.")

    # Create an instance of vtkMoleculeAppend
    append_filter = vtki.new("MoleculeAppend")

    # Add each molecule's vtkMolecule to the append filter
    for mol in molecules:
        if not isinstance(mol, Molecule):
            raise TypeError(
                "append_molecules() expects an iterable of Molecule objects."
            )
        append_filter.AddInputData(mol.molecule)

    # Update the filter to generate the combined molecule
    append_filter.Update()

    # Get the output molecule from the filter
    combined_vtk_molecule = append_filter.GetOutput()

    # Create a new Molecule object
    combined_molecule = Molecule()

    # Set the combined vtkMolecule to the new Molecule object
    combined_molecule.molecule = combined_vtk_molecule

    # Reconfigure the mapper and actor
    combined_molecule.mapper.SetInputData(combined_vtk_molecule)
    combined_molecule.actor.SetMapper(combined_molecule.mapper)

    # Optionally, copy rendering settings from the first molecule
    if molecules:
        first_mol = molecules[0]
        if first_mol.mapper.GetRenderAtoms():
            combined_molecule.use_ball_and_stick()
        else:
            combined_molecule.use_space_filling()
        combined_molecule.set_atom_radius_scale(
            first_mol.mapper.GetAtomicRadiusScaleFactor()
        )
        combined_molecule.set_bond_radius(first_mol.mapper.GetBondRadius())

    return combined_molecule