diff --git a/src/python/espressomd/visualization_opengl.py b/src/python/espressomd/visualization_opengl.py index 9b4b7588fe4..48004a36fb7 100644 --- a/src/python/espressomd/visualization_opengl.py +++ b/src/python/espressomd/visualization_opengl.py @@ -663,7 +663,7 @@ def update(self): self.specs['update_fps']: # ES UPDATES WHEN SYSTEM HAS PROPAGATED. ALSO UPDATE ON PAUSE # FOR PARTICLE INFO - self._update_particles() + self._update_particles_and_bonds() # LB UPDATE if self.specs['LB_draw_velocity_plane']: @@ -701,10 +701,9 @@ def update(self): # FETCH DATA ON STARTUP def _initial_espresso_updates(self): - self._update_particles() + self._update_particles_and_bonds() if self.has_particle_data['charge']: self._update_charge_color_range() - self._update_bonds() if self.specs['draw_constraints']: self._update_constraints() if self.specs['draw_cells'] or self.specs['draw_nodes']: @@ -712,34 +711,54 @@ def _initial_espresso_updates(self): if self.specs['draw_cells']: self._update_cells() + def _update_particles_and_bonds(self): + """Fetch particle data from core and put it in the data dicts + used by the visualizer. + + """ + parts_from_core = self.system.part.all() + self._update_particles(parts_from_core) + self._update_bonds(parts_from_core) + # GET THE PARTICLE DATA - def _update_particles(self): + def _update_particles(self, particle_data): + """Updates particle data used for drawing particles. + Do not call directly but use _update_particles_and_bonds()! - self.particles['pos'] = self.system.part.all().pos_folded - self.particles['type'] = self.system.part.all().type + """ + self.particles['pos'] = particle_data.pos_folded + self.particles['type'] = particle_data.type + # particle ids can be discontiguous, therefore create a dict that maps + # particle ids to respective index in data arrays (used by + # _draw_bonds()) + self.index_from_id = { + id: ndx for ndx, id in enumerate( + particle_data.id)} if self.has_particle_data['velocity']: - self.particles['velocity'] = self.system.part.all().v + self.particles['velocity'] = particle_data.v if self.has_particle_data['force']: - self.particles['force'] = self.system.part.all().f + self.particles['force'] = particle_data.f if self.has_particle_data['ext_force']: - self.particles['ext_force'] = self.system.part.all().ext_force + self.particles['ext_force'] = particle_data.ext_force if self.has_particle_data['charge']: - self.particles['charge'] = self.system.part.all().q + self.particles['charge'] = particle_data.q if self.has_particle_data['director']: - self.particles['director'] = self.system.part.all().director + self.particles['director'] = particle_data.director if self.has_particle_data['node']: - self.particles['node'] = self.system.part.all().node + self.particles['node'] = particle_data.node if self.info_id != -1: + # data array index of particle with id info_id + info_index = self.index_from_id[self.info_id] for attr in self.particle_attributes: - self.highlighted_particle[attr] = getattr( - self.system.part.by_id(self.info_id), attr) + self.highlighted_particle[attr] = \ + getattr(particle_data, attr)[info_index] def _update_lb_velocity_plane(self): if self.lb_is_cpu: @@ -881,20 +900,31 @@ def shape_arguments(shape, part_type): self.shapes.append(Shape(*arguments)) # GET THE BOND DATA, SO FAR CALLED ONCE UPON INITIALIZATION - def _update_bonds(self): + def _update_bonds(self, particle_data): + """Update bond data used for drawing bonds. + Do not call directily but use _update_particles_and_bonds()! + + Updates data array self.bonds[]; structure of elements is: + [, + , + 0: - v = self.particles[prop][part_id] + v = self.particles[prop][self.index_from_id[part_id]] col = self._modulo_indexing(type_colors, part_type) radius = self._modulo_indexing(type_radii, part_type) draw_arrow( - self.particles['pos'][part_id], np.array(v, dtype=float) * sc, + self.particles['pos'][self.index_from_id[part_id] + ], np.array(v, dtype=float) * sc, radius, col, self.materials['chrome'], self.specs['quality_arrows']) def _draw_bonds(self): @@ -1137,8 +1167,14 @@ def _draw_bonds(self): self.specs['bond_type_materials'], b[2])] radius = self._modulo_indexing( self.specs['bond_type_radius'], b[2]) - x_a = self.particles['pos'][b[0]] - x_b = self.particles['pos'][b[1]] + # if particles are deleted in the core, index_from_id might + # throw a KeyError -- e. g. when deleting a bond created by + # collision_detection (using virtual site particles) + try: + x_a = self.particles['pos'][self.index_from_id[b[0]]] + x_b = self.particles['pos'][self.index_from_id[b[1]]] + except BaseException: + pass dx = x_b - x_a if abs(dx[0]) < box_l_2[0] and abs(