digichem-core 6.1.0__py3-none-any.whl → 6.10.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- digichem/__init__.py +2 -2
- digichem/config/base.py +6 -4
- digichem/data/batoms/batoms-renderer.py +190 -50
- digichem/data/batoms/batoms_renderer.py +500 -0
- digichem/file/base.py +14 -0
- digichem/file/cube.py +185 -16
- digichem/file/types.py +1 -0
- digichem/image/render.py +149 -48
- digichem/image/vmd.py +7 -2
- digichem/input/digichem_input.py +2 -2
- digichem/memory.py +10 -0
- digichem/misc/io.py +95 -1
- digichem/parse/__init__.py +6 -1
- digichem/parse/base.py +85 -54
- digichem/parse/cclib.py +139 -13
- digichem/parse/dump.py +3 -3
- digichem/parse/orca.py +1 -0
- digichem/parse/pyscf.py +35 -0
- digichem/parse/turbomole.py +3 -3
- digichem/parse/util.py +146 -65
- digichem/result/excited_state.py +17 -11
- digichem/result/metadata.py +307 -3
- digichem/result/result.py +3 -0
- digichem/result/spectroscopy.py +42 -0
- digichem/test/conftest.py +5 -0
- digichem/test/mock/cubegen +87172 -0
- digichem/test/mock/formchk +9456 -0
- digichem/test/test_image.py +54 -42
- digichem/test/test_memory.py +33 -0
- digichem/test/test_parsing.py +68 -1
- digichem/test/test_result.py +1 -1
- digichem/test/util.py +4 -1
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/METADATA +4 -3
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/RECORD +37 -32
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/WHEEL +1 -1
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/licenses/COPYING.md +0 -0
- {digichem_core-6.1.0.dist-info → digichem_core-6.10.3.dist-info}/licenses/LICENSE +0 -0
digichem/__init__.py
CHANGED
|
@@ -20,7 +20,7 @@ from digichem.datas import get_resource
|
|
|
20
20
|
# development = prerelease is not None
|
|
21
21
|
# # The full version number of this package.
|
|
22
22
|
# __version__ = "{}.{}.{}{}".format(major_version, minor_version, revision, "-pre.{}".format(prerelease) if development else "")
|
|
23
|
-
__version__ = "6.
|
|
23
|
+
__version__ = "6.10.3"
|
|
24
24
|
_v_parts = __version__.split("-")[0].split(".")
|
|
25
25
|
major_version = int(_v_parts[0])
|
|
26
26
|
minor_version = int(_v_parts[1])
|
|
@@ -39,7 +39,7 @@ __author__ = [
|
|
|
39
39
|
]
|
|
40
40
|
|
|
41
41
|
# Program date (when we were last updated). This is changed automatically.
|
|
42
|
-
_last_updated_string = "
|
|
42
|
+
_last_updated_string = "09/06/2025"
|
|
43
43
|
last_updated = datetime.strptime(_last_updated_string, "%d/%m/%Y")
|
|
44
44
|
|
|
45
45
|
# The sys attribute 'frozen' is our flag, '_MEIPASS' is the dir location.
|
digichem/config/base.py
CHANGED
|
@@ -33,6 +33,7 @@ class Digichem_options(Configurable):
|
|
|
33
33
|
help = "Options specifying paths to various external programs that digichem may use. If no path is given, then these programs will simply be executed by name (so relying on OS path resolution to find the necessary executables, which is normally fine.)",
|
|
34
34
|
formchk = Option(help = "Gaussian's formchk utility https://gaussian.com/formchk/", default = "formchk"),
|
|
35
35
|
cubegen = Option(help = "Gaussian's cubegen utility https://gaussian.com/cubegen/", default = "cubegen"),
|
|
36
|
+
cubegen_parallel = Option(help = "What type of parallelism to use with cubegen, multithreaded runs a single instance of cubegen across multiple CPUs, pool runs multiple instances of cubegen", choices = [None, "multithreaded", "pool"], default = "pool")
|
|
36
37
|
)
|
|
37
38
|
|
|
38
39
|
skeletal_image = Options(
|
|
@@ -70,10 +71,11 @@ Possible options are:
|
|
|
70
71
|
),
|
|
71
72
|
),
|
|
72
73
|
batoms = Options(help = "Beautiful Atoms/Blender specific options (only applies if engine == 'batoms'",
|
|
73
|
-
blender = Option(help = "Path to the blender executable, in which beautiful atoms should be installed", default =
|
|
74
|
-
cpus = Option(help = "The number of CPUs/threads to use. This option is overridden if running in a calculation
|
|
75
|
-
render_samples = Option(help = "The number of render samples (or passes) to use. Higher values result in higher image quality and greater render times", type = int, default =
|
|
76
|
-
perspective = Option(help = "The perspective mode", choices = ["orthographic", "perspective"], default = "
|
|
74
|
+
blender = Option(help = "Path to the blender executable, in which beautiful atoms should be installed", default = "batoms-blender"),
|
|
75
|
+
cpus = Option(help = "The number of CPUs/threads to use. This option is overridden if running in a calculation environment (where it uses the same number of CPUs as the calculation did)", type = int, default = 1),
|
|
76
|
+
render_samples = Option(help = "The number of render samples (or passes) to use. Higher values result in higher image quality and greater render times", type = int, default = 32),
|
|
77
|
+
perspective = Option(help = "The perspective mode", choices = ["orthographic", "perspective"], default = "perspective"),
|
|
78
|
+
stacking = Option(help = "The number of image copies to composite together to avoid transparency artifacts", type = int, default = 10)
|
|
77
79
|
# TODO: Colour options.
|
|
78
80
|
),
|
|
79
81
|
safe_cubes = Option(help = "Whether to sanitize cubes so older software can parse them (VMD < 1.9.2 etc)", type = bool, default = False),
|
|
@@ -7,6 +7,16 @@
|
|
|
7
7
|
#
|
|
8
8
|
# Where 'blender' is the path to the Beautiful Atoms Blender executable.
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
# import debugpy
|
|
12
|
+
# debugpy.listen(5678)
|
|
13
|
+
# debugpy.wait_for_client()
|
|
14
|
+
|
|
15
|
+
import addon_utils
|
|
16
|
+
def handle_error(exception):
|
|
17
|
+
raise exception
|
|
18
|
+
addon_utils.enable("batoms", handle_error = handle_error, default_set=True)
|
|
19
|
+
|
|
10
20
|
import sys
|
|
11
21
|
import argparse
|
|
12
22
|
import itertools
|
|
@@ -19,6 +29,50 @@ import logging
|
|
|
19
29
|
import ase.io
|
|
20
30
|
from batoms import Batoms
|
|
21
31
|
from batoms.utils.butils import object_mode
|
|
32
|
+
from batoms.render import Render
|
|
33
|
+
|
|
34
|
+
class Digichem_render(Render):
|
|
35
|
+
def set_viewport_distance_center(self, center=None, padding=0, canvas=None):
|
|
36
|
+
"""
|
|
37
|
+
Calculate canvas and direction
|
|
38
|
+
"""
|
|
39
|
+
batoms = self.batoms
|
|
40
|
+
if padding is None:
|
|
41
|
+
padding = max(batoms.size) + 0.5
|
|
42
|
+
if center is None:
|
|
43
|
+
center = batoms.get_center_of_geometry()
|
|
44
|
+
self.center = center
|
|
45
|
+
if canvas is None:
|
|
46
|
+
width, height, depth = batoms.get_canvas_box(
|
|
47
|
+
direction=self.viewport, padding=padding
|
|
48
|
+
)
|
|
49
|
+
else:
|
|
50
|
+
width = canvas[0]
|
|
51
|
+
height = canvas[1]
|
|
52
|
+
depth = canvas[2]
|
|
53
|
+
if self.distance < 0:
|
|
54
|
+
self.distance = max(10, depth)
|
|
55
|
+
|
|
56
|
+
self.update_camera(width, height, depth / 2)
|
|
57
|
+
|
|
58
|
+
# To auto centre the camera, we need to select the molecule as well as all isosurfaces that might be present.
|
|
59
|
+
# Select the molecule.
|
|
60
|
+
self.batoms.obj.select_set(True)
|
|
61
|
+
|
|
62
|
+
# Isosurfaces.
|
|
63
|
+
for obj in self.batoms.coll.all_objects:
|
|
64
|
+
if obj.batoms.type == "ISOSURFACE":
|
|
65
|
+
obj.select_set(True)
|
|
66
|
+
|
|
67
|
+
# Set camera as active.
|
|
68
|
+
bpy.context.scene.camera = self.camera.obj
|
|
69
|
+
|
|
70
|
+
# Manually set a focal point.
|
|
71
|
+
self.camera.lens = 50
|
|
72
|
+
# Auto centre.
|
|
73
|
+
bpy.ops.view3d.camera_to_view_selected()
|
|
74
|
+
|
|
75
|
+
self.update_light()
|
|
22
76
|
|
|
23
77
|
def add_molecule(
|
|
24
78
|
cube_file,
|
|
@@ -55,25 +109,41 @@ def add_molecule(
|
|
|
55
109
|
mol = Batoms(name, from_ase = cube["atoms"])
|
|
56
110
|
|
|
57
111
|
# Set some look and feel options.
|
|
58
|
-
# Change molecule style.
|
|
59
|
-
mol.model_style = 1
|
|
60
112
|
|
|
61
113
|
# Hide cell boundaries.
|
|
62
114
|
mol.cell.hide = True
|
|
63
115
|
|
|
64
116
|
# Colour tuning.
|
|
65
117
|
# Carbon to 'black'.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
118
|
+
new_colors = {
|
|
119
|
+
"C": (0.095, 0.095, 0.095, 1),
|
|
120
|
+
"B": (1.0, 0.396, 0.468, 1)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for atom, color in new_colors.items():
|
|
124
|
+
try:
|
|
125
|
+
mol[atom].color = color
|
|
126
|
+
except AttributeError:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
# And bonds.
|
|
130
|
+
for key, value in mol.bond.settings.items():
|
|
131
|
+
for atom, color in new_colors.items():
|
|
132
|
+
if key[0] == atom:
|
|
133
|
+
value['color1'] = color
|
|
134
|
+
|
|
135
|
+
if key[1] == "C":
|
|
136
|
+
value['color2'] = color
|
|
74
137
|
|
|
75
|
-
|
|
76
|
-
|
|
138
|
+
# Change molecule style.
|
|
139
|
+
mol.model_style = 1
|
|
140
|
+
|
|
141
|
+
# Slightly increase volume of all atoms.
|
|
142
|
+
for atom in mol.species.keys():
|
|
143
|
+
mol[atom].scale *= 1.25
|
|
144
|
+
|
|
145
|
+
# Increase volume of H atoms
|
|
146
|
+
mol['H'].scale = 0.75
|
|
77
147
|
|
|
78
148
|
# Add volumes.
|
|
79
149
|
if len(surface_settings) != 0:
|
|
@@ -85,15 +155,9 @@ def add_molecule(
|
|
|
85
155
|
mol.isosurface.draw()
|
|
86
156
|
|
|
87
157
|
# Now move the entire molecule (isosurface and all) back to the origin.
|
|
88
|
-
mol.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# For some reason, this code moves the bond objects to a new location?
|
|
92
|
-
# object_mode()
|
|
93
|
-
# bpy.ops.object.select_all(action='DESELECT')
|
|
94
|
-
# mol.obj.select_set(True)
|
|
95
|
-
# bpy.context.view_layer.objects.active = mol.obj
|
|
96
|
-
# bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
|
|
158
|
+
mol.obj.select_set(True)
|
|
159
|
+
bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center='MEDIAN')
|
|
160
|
+
bpy.context.object.location = [0,0,0]
|
|
97
161
|
|
|
98
162
|
# If we have any rotations, apply those.
|
|
99
163
|
for axis, angle in rotations:
|
|
@@ -123,6 +187,9 @@ def add_molecule(
|
|
|
123
187
|
bpy.ops.transform.rotate(value=angle, orient_axis=axis.upper(),
|
|
124
188
|
center_override = (0,0,0))
|
|
125
189
|
|
|
190
|
+
if not visible:
|
|
191
|
+
mol.hide = True
|
|
192
|
+
|
|
126
193
|
return mol
|
|
127
194
|
|
|
128
195
|
# Adapted from https://blender.stackexchange.com/questions/5898/how-can-i-create-a-cylinder-linking-two-points-with-python?newreg=f372ba9448694f5b97879a6dab963cee
|
|
@@ -173,7 +240,13 @@ def draw_primitive(start, end, radius, mesh_type, color, collection = None):
|
|
|
173
240
|
bsdf = nodes["Principled BSDF"]
|
|
174
241
|
bsdf.inputs["Base Color"].default_value = color
|
|
175
242
|
bsdf.inputs["Metallic"].default_value = 0.1
|
|
176
|
-
|
|
243
|
+
try:
|
|
244
|
+
# Blener 3.x
|
|
245
|
+
bsdf.inputs["Specular"].default_value = 0.2
|
|
246
|
+
except KeyError:
|
|
247
|
+
# Blender 4.x
|
|
248
|
+
bsdf.inputs["Specular IOR Level"].default_value = 0.2
|
|
249
|
+
|
|
177
250
|
bsdf.inputs["Roughness"].default_value = 0.2
|
|
178
251
|
mat.diffuse_color = color
|
|
179
252
|
|
|
@@ -186,6 +259,8 @@ def draw_primitive(start, end, radius, mesh_type, color, collection = None):
|
|
|
186
259
|
# Link each object to the target collection
|
|
187
260
|
collection.objects.link(obj)
|
|
188
261
|
|
|
262
|
+
return obj
|
|
263
|
+
|
|
189
264
|
|
|
190
265
|
def draw_arrow(start, end, radius, color, split = 0.9, collection = None):
|
|
191
266
|
# Decide what proportion of the total vector to dedicate to the arrow stem and head.
|
|
@@ -195,8 +270,23 @@ def draw_arrow(start, end, radius, color, split = 0.9, collection = None):
|
|
|
195
270
|
dist = math.sqrt(dx**2 + dy**2 + dz**2)
|
|
196
271
|
|
|
197
272
|
join = (dx * split + start[0], dy * split + start[1], dz * split + start[2])
|
|
198
|
-
draw_primitive(start, join, radius, "cylinder", color, collection = collection)
|
|
199
|
-
draw_primitive(join, end, radius*2, "cone", color, collection = collection)
|
|
273
|
+
cylinder = draw_primitive(start, join, radius, "cylinder", color, collection = collection)
|
|
274
|
+
cone = draw_primitive(join, end, radius*2, "cone", color, collection = collection)
|
|
275
|
+
|
|
276
|
+
# Select the two objects and join them together.
|
|
277
|
+
bpy.ops.object.select_all(action='DESELECT')
|
|
278
|
+
cylinder.select_set(True)
|
|
279
|
+
cone.select_set(True)
|
|
280
|
+
bpy.ops.object.join()
|
|
281
|
+
|
|
282
|
+
arrow = cone
|
|
283
|
+
|
|
284
|
+
# Set the origin of the new combined object to the origin of the arrow.
|
|
285
|
+
bpy.context.scene.cursor.location = start
|
|
286
|
+
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
|
|
287
|
+
|
|
288
|
+
return arrow
|
|
289
|
+
|
|
200
290
|
|
|
201
291
|
|
|
202
292
|
def main():
|
|
@@ -205,25 +295,25 @@ def main():
|
|
|
205
295
|
description='Render images with BAtoms')
|
|
206
296
|
|
|
207
297
|
parser.add_argument("cube_file", help = "Path to the cube file to read")
|
|
208
|
-
parser.add_argument("output", help = "File to write to")
|
|
298
|
+
parser.add_argument("output", help = "File to write to", nargs="?", default = None)
|
|
209
299
|
parser.add_argument("--second_cube", help = "Optional second cube file to read additional isosurface data from", default = None)
|
|
210
300
|
parser.add_argument("--isovalues", help = "List of isovalues to render", nargs = "*", type = float, default = [])
|
|
211
301
|
parser.add_argument("--isotype", help = "Whether to render positive, negative or both isosurfaces for each isovalue", choices = ["positive", "negative", "both"], default = "both")
|
|
212
302
|
parser.add_argument("--isocolor", help = "The colouring method to use for isosurfaces", choices = ["sign", "cube"], default = "sign")
|
|
213
|
-
parser.add_argument("--primary-color", help = "RGBA for one of the colors to use for isosurfaces", type = float, nargs = 4, default = [0.1, 0.1, 0.9, 0.
|
|
214
|
-
parser.add_argument("--secondary-color", help = "RGBA for the other color to use for isosurfaces", type = float, nargs = 4, default = [1, 0.058, 0.0, 0.
|
|
215
|
-
parser.add_argument("--style", help = "Material style for isosurfaces", choices = ('default', 'metallic', 'plastic', 'ceramic', 'mirror'), default = "
|
|
303
|
+
parser.add_argument("--primary-color", help = "RGBA for one of the colors to use for isosurfaces", type = float, nargs = 4, default = [0.1, 0.1, 0.9, 0.5])
|
|
304
|
+
parser.add_argument("--secondary-color", help = "RGBA for the other color to use for isosurfaces", type = float, nargs = 4, default = [1, 0.058, 0.0, 0.5])
|
|
305
|
+
parser.add_argument("--style", help = "Material style for isosurfaces", choices = ('default', 'metallic', 'plastic', 'ceramic', 'mirror'), default = "ceramic")
|
|
216
306
|
parser.add_argument("--cpus", help = "Number of parallel CPUs to use for rendering", type = int, default = 1)
|
|
217
307
|
parser.add_argument("--use-gpu", help = "Whether to enable GPU rendering", action = "store_true")
|
|
218
|
-
parser.add_argument("--orientation", help = "The orientation to render from, as x, y, z values", nargs = 3, type = float, default = [0, 0,
|
|
308
|
+
parser.add_argument("--orientation", help = "The orientation to render from, as x, y, z values", nargs = 3, type = float, default = [0, 0, 0])
|
|
219
309
|
parser.add_argument("--resolution", help = "The output resolution in px", type = int, default = 1024)
|
|
220
|
-
parser.add_argument("--render-samples", help = "The maximum number of render samples, more generally results in higher quality but longer render times", type = int, default = 256)
|
|
310
|
+
parser.add_argument("--render-samples", help = "The maximum number of render samples, more generally results in higher quality but longer render times", type = int, default = 64)# default = 256)
|
|
221
311
|
parser.add_argument("--rotations", help = "A list of rotations (in JSON) to rotate the molecule to a given alignment. The first item in each list item is the axis to rotate about (0=x, 1=y, 2=z), the second is the angle to rotate by (in radians)", nargs = "*", default = [])
|
|
222
312
|
parser.add_argument("--dipoles", help = "Draw dipoles from a list of the following data (in JSON): 0) start coord, 1) end coord, 2) RGBA color information", nargs = "*", default = [])
|
|
223
|
-
parser.add_argument("--alpha", help = "Override the opacity value for all molecule objects (but not dipoles) to this value, useful for showing dipole arrows more clearly", default = None, type = float)
|
|
313
|
+
parser.add_argument("--alpha", help = "Override the opacity value for all molecule objects (but not dipoles) to 1- this value, useful for showing dipole arrows more clearly", default = None, type = float)
|
|
224
314
|
parser.add_argument("--perspective", help = "The perspective mode, either orthographic or perspective", default = "perspective", choices = ["perspective", "orthographic"])
|
|
225
315
|
parser.add_argument("--padding", help = "Padding", type = float, default = 1.0)
|
|
226
|
-
|
|
316
|
+
parser.add_argument("--multi", help = "Render multiple images, each of a different angle of the scene. Each argument should consist of 6 parts, the x y z position, the resolution, the samples, and the filename (which is appended to 'output')", nargs = 6, default =[], action="append")
|
|
227
317
|
# Both blender and python share the same command line arguments.
|
|
228
318
|
# They are separated by double dash ('--'), everything before is for blender,
|
|
229
319
|
# everything afterwards is for python (except for the first argument, wich is
|
|
@@ -237,11 +327,21 @@ def main():
|
|
|
237
327
|
|
|
238
328
|
# Batoms or blender will silently set the extension to png if it's not already.
|
|
239
329
|
# This is surprising, so stop now before that happens.
|
|
240
|
-
if Path(args.output).suffix.lower() != ".png":
|
|
330
|
+
if args.output is not None and Path(args.output).suffix.lower() != ".png":
|
|
241
331
|
raise ValueError("Output location must have a .png extension")
|
|
242
332
|
|
|
243
333
|
if args.rotations is not None:
|
|
244
334
|
rotations = [yaml.safe_load(rotation) for rotation in args.rotations]
|
|
335
|
+
|
|
336
|
+
if args.multi != []:
|
|
337
|
+
if args.orientation != [0, 0, 0]:
|
|
338
|
+
raise ValueError("You cannot set both --orientation and --multi!")
|
|
339
|
+
|
|
340
|
+
if args.resolution != 1024:
|
|
341
|
+
raise ValueError("You cannot set both --resolution and --multi!")
|
|
342
|
+
|
|
343
|
+
if args.output is not None:
|
|
344
|
+
raise ValueError("You cannot set both 'output' and --multi!")
|
|
245
345
|
|
|
246
346
|
# Remove the starting cube object.
|
|
247
347
|
bpy.ops.object.select_all(action='SELECT')
|
|
@@ -250,7 +350,7 @@ def main():
|
|
|
250
350
|
# Load the input data.
|
|
251
351
|
mol = add_molecule(
|
|
252
352
|
args.cube_file,
|
|
253
|
-
name = "
|
|
353
|
+
name = "first_mol",
|
|
254
354
|
visible = True,
|
|
255
355
|
rotations = rotations,
|
|
256
356
|
isovalues = args.isovalues,
|
|
@@ -263,13 +363,14 @@ def main():
|
|
|
263
363
|
# Uncomment to show atom labels.
|
|
264
364
|
# Needs some tweaking to appear in render (viewport only by default).
|
|
265
365
|
#mol.show_label = 'species'
|
|
366
|
+
mol2 = None
|
|
266
367
|
|
|
267
368
|
# If we have a second cube, add that too.
|
|
268
369
|
if args.second_cube is not None:
|
|
269
370
|
mol2 = add_molecule(
|
|
270
371
|
args.second_cube,
|
|
271
|
-
name = "
|
|
272
|
-
visible =
|
|
372
|
+
name = "second_mol",
|
|
373
|
+
visible = False,
|
|
273
374
|
rotations = rotations,
|
|
274
375
|
isovalues = args.isovalues,
|
|
275
376
|
isotype = args.isotype,
|
|
@@ -282,29 +383,27 @@ def main():
|
|
|
282
383
|
# Set all materials transparent.
|
|
283
384
|
for material in bpy.data.materials:
|
|
284
385
|
try:
|
|
285
|
-
material.node_tree.nodes['Principled BSDF'].inputs['Alpha'].default_value = args.alpha
|
|
386
|
+
material.node_tree.nodes['Principled BSDF'].inputs['Alpha'].default_value = (1 - args.alpha)
|
|
286
387
|
except Exception as e:
|
|
287
388
|
pass
|
|
288
389
|
|
|
289
390
|
|
|
290
391
|
# Draw any dipoles.
|
|
392
|
+
arrows = []
|
|
291
393
|
if args.dipoles is not None:
|
|
292
394
|
|
|
293
395
|
dipoles = [yaml.safe_load(dipole) for dipole in args.dipoles]
|
|
294
396
|
for start_coord, end_coord, rgba in dipoles:
|
|
295
|
-
draw_arrow(start_coord, end_coord, 0.
|
|
397
|
+
arrows.append(draw_arrow(start_coord, end_coord, 0.1, rgba, collection = mol.coll))
|
|
296
398
|
|
|
297
399
|
|
|
298
400
|
# Setup rendering settings.
|
|
299
401
|
# mol.render.engine = 'workbench'
|
|
300
402
|
# mol.render.engine = 'eevee'
|
|
301
403
|
mol.render.engine = 'cycles'
|
|
302
|
-
mol.render.resolution = [args.resolution, args.resolution]
|
|
303
404
|
# Set up cycles for good quality rendering.
|
|
304
405
|
# Prevents early end to rendering (forces us to use the actual number of samples).
|
|
305
406
|
bpy.context.scene.cycles.use_adaptive_sampling = False
|
|
306
|
-
# Quality control, more = better and slower.
|
|
307
|
-
bpy.context.scene.cycles.samples = args.render_samples
|
|
308
407
|
# Post-processing to remove noise, works well for coloured backgrounds, useless for transparency.
|
|
309
408
|
bpy.context.scene.cycles.use_denoising = True
|
|
310
409
|
# Ray-tracing options
|
|
@@ -316,8 +415,23 @@ def main():
|
|
|
316
415
|
# Use maximum compression.
|
|
317
416
|
bpy.context.scene.render.image_settings.compression = 1000
|
|
318
417
|
|
|
418
|
+
|
|
319
419
|
# Change light intensity.
|
|
320
|
-
|
|
420
|
+
mol.render.lights["Default"].direction = [0.1, 0.1, 1]
|
|
421
|
+
mol.render.lights["Default"].obj.data.node_tree.nodes["Emission"].inputs[1].default_value = 0.2
|
|
422
|
+
mol.render.lights["Default"].obj.data.angle = 0.174533
|
|
423
|
+
|
|
424
|
+
# Add a second light for depth.
|
|
425
|
+
mol.render.lights.add("Accent1", direction = [1,0.5,0.75])
|
|
426
|
+
mol.render.lights.add("Accent2", direction = [0.5,1,0.75])
|
|
427
|
+
|
|
428
|
+
mol.render.lights["Accent1"].obj.data.angle = 0.0872665
|
|
429
|
+
mol.render.lights["Accent1"].obj.data.node_tree.nodes["Emission"].inputs[1].default_value = 0.25
|
|
430
|
+
mol.render.lights["Accent2"].obj.data.angle = 0.0872665
|
|
431
|
+
mol.render.lights["Accent2"].obj.data.node_tree.nodes["Emission"].inputs[1].default_value = 0.25
|
|
432
|
+
|
|
433
|
+
# bpy.data.lights["batoms_light_Default"].node_tree.nodes["Emission"].inputs[1].default_value = 0.45
|
|
434
|
+
# bpy.data.lights["batoms_light_Default"].angle
|
|
321
435
|
#mol.render.lights["Default"].energy=10
|
|
322
436
|
|
|
323
437
|
# Change view mode.
|
|
@@ -339,14 +453,40 @@ def main():
|
|
|
339
453
|
bpy.context.scene.render.threads_mode = 'FIXED'
|
|
340
454
|
bpy.context.scene.render.threads = args.cpus
|
|
341
455
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
#
|
|
346
|
-
#
|
|
347
|
-
#
|
|
348
|
-
#
|
|
349
|
-
#
|
|
456
|
+
# Set our custom renderer so we can modify zoom etc.
|
|
457
|
+
mol.render = Digichem_render()
|
|
458
|
+
|
|
459
|
+
# We have two ways we can change which angle we render from.
|
|
460
|
+
# 1) the viewport keyword arg (which places the camera in a certain location).
|
|
461
|
+
# 2) rotate the molecule.
|
|
462
|
+
#
|
|
463
|
+
# We use option 2, because this gives us more control.
|
|
464
|
+
|
|
465
|
+
# Work out how many angles we're rendering from.
|
|
466
|
+
if args.multi == []:
|
|
467
|
+
# Just one.
|
|
468
|
+
targets = [[args.orientation[0], args.orientation[1], args.orientation[2], args.resolution, args.render_samples, args.output]]
|
|
469
|
+
|
|
470
|
+
else:
|
|
471
|
+
# More than one.
|
|
472
|
+
targets = args.multi
|
|
473
|
+
|
|
474
|
+
for x, y, z, resolution, samples, full_file_name in targets:
|
|
475
|
+
# Add args.output and mini_file_name together (useful for --multi).
|
|
476
|
+
orientation = (float(x), float(y), float(z))
|
|
477
|
+
|
|
478
|
+
mol.render.resolution = [resolution, resolution]
|
|
479
|
+
# Quality control, more = better and slower.
|
|
480
|
+
bpy.context.scene.cycles.samples = int(samples)
|
|
481
|
+
mol.obj.delta_rotation_euler = orientation
|
|
482
|
+
|
|
483
|
+
if mol2 is not None:
|
|
484
|
+
mol2.obj.delta_rotation_euler = orientation
|
|
485
|
+
|
|
486
|
+
for arrow in arrows:
|
|
487
|
+
arrow.delta_rotation_euler = orientation
|
|
488
|
+
|
|
489
|
+
mol.get_image(viewport = [0,0,1], output = full_file_name, padding = args.padding)
|
|
350
490
|
|
|
351
491
|
return 0
|
|
352
492
|
|