POVRAY Visualisation Help

Hi there,

Until now, I have been producing povray renders of atoms objects using the ASE GUI Render Scene tool.
I am now trying to automate this process and make it easier to standardise views.

I am using the method described on File input and output, particularly in the save_pov.py and saving_graphics.py example files:

write('atoms.pov', atoms, **generic_projection_settings,
povray_settings=povray_settings).render()

With generic_projection_settings and povray_settings have been defined as:

generic_projection_settings = {
    'rotation': '90x,80y,90z',
    'radii': 1.0,
    'colors': None,
    'show_unit_cell': 1,
}

povray_settings = {
    'transparent': False,
    'camera_type': 'perspective',
    'textures': ['jmol' for atom in atoms],
    'canvas_width': 500,
    'camera_dist': 100,
}

There doesn’t seem to be much guidance on this in the ASE documentation or in the POVRAY documentation, so I think I have a few questions on this method.

  1. Settings in generic_projection_settings and povray_settings don’t appear to be interchangeable between dictionaries or combinable into one dictionary. What is the difference between the two and is there a list of possible settings/keywords which each one accepts? I have found lots of possible settings, but none which differentiate between the two.

  2. My IDE also doesn’t recognise the .render() method and there doesn’t seem to be anything said about this anywhere. I think that this produces the .png files from the .pov and .ini files, but can anyone explain what the .render() method is actually doing/using?

The main thing I am trying to do with this function is to visualise trajectory files. I currently render the atoms object in each .traj frame in POVRAY and then combine the png files into a gif using the convert linux command.

  1. On the off-chance there is one, is there a quick solution to visualising trajectory files in this way? e.g. in the ASE GUI, there was the “render all frames” option. Is there a way to “render all frames” with this method without calling the function individually for each frame in the trajectory file as I have been doing?

  2. Related to question 2, when I call it individually for each frame, the resolution changes for each image. I’m assuming this is because I have set the canvas_width setting, but when I don’t set that option, it produces 64x38 pixel images… Is there a way to make the image sizes consistent?

nh3-h3o.00

  1. Another problem I had was that the atoms objects sometimes went off one side of the images. My solution to this was to try to zoom out using the camera_dist setting. However, this doesn’t seem to change anything in the final png images. 50 seems to be a default value in these examples, but even when I significantly change it, the only change seems to be the camera location in the .pov file, whilst the resulting .png images are identical, perfectly fitting the atoms object in the first frame. The only time I have got the image to change was using the ‘perspective’ camera type and putting the camera obscenely close to the atoms to get a very warped image. Can anyone explain what the camera_dist setting does and suggest how I can zoom out in my images in the same way I was able to in the ASE GUI?

This is the sort of image I am getting out of this method:

nh3-h3o.00

The only change has been setting camera_dist to something like a value of 1, which gives this:

nh3-h3o.00

Alternatively, if anyone knows of a helpful resource with lots of help with this, that would be amazing. I have searched for a while for one, but only found the examples in the ASE documentation (which, while helpful for showing the code, don’t explain too much…) and the POVRAY documentation (which certainly has a lot of info, but is hard to sift through and, in my mind, doesn’t match up well with what I can see ASE doing…) A simple walkthrough of the methods and possible settings would be fantastic!

I think that’s probably enough questions for now, although I’ll probably have more later.
I’m really grateful for any help with this.

Thanks so much,
Matt

Hi Matt,

generic_projection_settings should contain parameters for PlottingVariables which is the object that should handle camera transformations and picture size information for plotting atoms in MPL or rendering them in POV-ray. The povray_settings should be povray-specific parameters.

write_pov() creates a pvars with the projections settings and then creates a POVRAY object with those settings and povray specific settings here: ase/io/pov.py · master · ase / ase · GitLab

check here for more info on povray settings: ase/io/pov.py · master · ase / ase · GitLab
and here’s where PlottingVariables are poorly documented: ase/io/utils.py · master · ase / ase · GitLab

I am not happy with the way that current rendering system works which I why I’ve been slowly trying to rewrite it. Once I can finish a PlottingVariables overhaul, large chunks of the povray code will be made redundant and removed.

  1. POVRAY.render() calls the povray_executable command to read your .ini file which points to your .pov file. (ase/io/pov.py · master · ase / ase · GitLab)

  2. Once you’ve got your other povray settings defined in setting dicts, it’s a simple for loop, so no there is no function for it. I’d recommend you use a file name format with zero padding of integers like fname='picture_%05d.pov'%num because FFMPEG and imagemagick play nice with zero padding.

  3. yes. Use orthographic cameras if you want anything to work nicely, camera angle in orthographic cameras have the effect of zooming in or out. You can do the same with camera distance. Here’s a chunk from one of my scripts:

style = 'simple'
color_dict = {
                'Nb' : [  23, 111, 208],
                'Zr' : [ 230,  83,  17],
                'O'  : [ 188, 133, 254],
                'Ti' : [  50, 229,  26],
                'X'  : [ 200, 200, 200]}

#rotation = '45x, -35.264y, 30z' # down <111>
rotation = '0x, 0y, 0z' # down <100>


radius_scale = 0.80

from ase.data import atomic_numbers, covalent_radii
radius_list = []
for atomic_number in atoms.get_atomic_numbers():
	if atomic_number == 8 :
		radius_list.append(0.8)
	else:
		radius_list.append(radius_scale*covalent_radii[atomic_number])
colors = np.array([ color_dict[atom.symbol] for atom in atoms ])/255

kwargs = { # For povray files only
	'transparent'  : True, # Transparent background
	'canvas_width' : 2560,  # Width of canvas in pixels
	'canvas_height': None, #None,  # Height of canvas in pixels
	#'image_height' : 22,
	#'image_width'  : 102, # I think these are in atomic units
	'camera_dist'  : 170.0,   # Distance from camera to front atom,
	'camera_type': 'orthographic angle 35',  # 'perspective angle 20'
	'area_light' : [(-1.0, -1.0, 200.), 'White', 22.0, 102.0, 20, 2],
	'radii' : radius_list,
	'textures': len(atoms)*[style],
	'depth_cueing':False,
	'colors':colors,
	}

# some more options:
#'image_plane'  : None,  # Distance from front atom to image plane
#                        # (focal depth for perspective)
#'camera_type'  : 'perspective', # perspective, ultra_wide_angle
#'point_lights' : [],             # [[loc1, color1], [loc2, color2],...]
#'area_light'   : [(2., 3., 40.) ,# location
#                  'White',       # color
#                  .7, .7, 3, 3], # width, height, Nlamps_x, Nlamps_y
#'background'   : 'White',        # color
#'celllinewidth': 0.05, # Radius of the cylinders representing the cell


generic_projection_settings = {
	'rotation': rotation,
	'radii': radius_list,
	'show_unit_cell': 2,
	#'extra_offset':(2.0, 0.0)
	}


from ase import io
povobj = io.write(
    pov_name,
    atoms, run_povray=run_povray,
    **generic_projection_settings,
    povray_settings=kwargs)

povobj.render()
  1. Currently, the image plane is set in PlottingVariables and is automatically set by the bounding box of the atoms, or atoms + cell, controlled by show_unit_cell 0, 1, or 2. You can manually enforce the image with bbox which is in paper-space units unless you set scale=1 and then you can move the slide image box in the image plane. Very awkward, I know and I am trying to fix it.
1 Like

Thanks so much for this reply. Really really helpful stuff.
I’ve been testing all of this recently to check that I understand it.
I’ll just go through your answers again.

  1. This makes a lot of sense now. Which setting belonged in which dict seemed arbitrary, but I understand now.

  2. Ah great, I couldn’t find that command anywhere when I was looking for it before. This explains why my IDE doesn’t recognise it, because I haven’t explicitly imported ase.io.pov.

  3. Thanks for the ideas. That’s very encouraging for me as a new programmer because I’m already doing all of your suggestions in my code! I’ll keep working on it now I know the rest of this.

    1. I think I’ll cover these together. In my tests, camera_dist will only work when you specify an angle for the orthographic camera_type. Previously I was just specifying ‘orthographic’ and that doesn’t zoom in/out with camera_dist (I imagine that’s because it’s compensating for camera_dist by changing the angle to cancel it out…), but camera_dist does work if I give it an angle. I’ve found that an angle of 5 and a camera_dist of 50 seem to be the defaults.

Just on you rewriting this code, I’m all for it, but could you please write some detailed documentation explaining the new method? All of what you’ve said above makes a lot of sense now with you having explained it, but there’s little chance that a new programmer like me could have figured any of that out just from the code…

Sorry for the long time for a reply, I’ve been away recently.
But like I say, really helpful stuff, thanks.

Matt

hi @mjwaters

Do you have plans to improve ASE’s GUI for better visualization and have some features such as, allowing to save *.avi from the Trajectory file?

Thank.