Skip to content

Commit 32e1b8c

Browse files
robertodrarnfinn
authored andcommitted
Add Python script to plot cavity from .npz file
The script plots planar polygons, much like GeomView. Given an additional npy file, it can color map the finite elements accordingly. A colorbar is also plotted, according to the color-coding implied by the surface function used. The codata.py and plot_cavity.py scripts are not configured by CMake anymore.
1 parent 411e935 commit 32e1b8c

File tree

7 files changed

+176
-7
lines changed

7 files changed

+176
-7
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,5 @@ doc/gfx/cloc_tools.py.in licensefile=.githooks/LICENSE-Python
7474
src/make_cmake_files.py licensefile=.githooks/LICENSE-Python
7575
tools/codata.py.in licensefile=.githooks/LICENSE-Python
7676
tools/pcmsolver.py.in licensefile=.githooks/LICENSE-Python
77+
tools/plot_cavity.py.in licensefile=.githooks/LICENSE-Python
7778
tests/make_cmake_files.py licensefile=.githooks/LICENSE-Python

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Change Log
22

3+
## [Unreleased]
4+
5+
### Added
6+
7+
- A Python script, using Matplotlib, to plot the cavity.
8+
The script can also color-map the finite elements according to the values of
9+
a surface function.
10+
311
## [Version 1.1.10] - 2017-03-27
412

513
### Changed

cmake/custom/autogenerated.cmake

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,11 @@ add_custom_command(
3838
add_custom_target(generate-pcmsolver-py ALL DEPENDS ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR}/pcmsolver.py)
3939
install(FILES ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR}/pcmsolver.py DESTINATION ${PYMOD_INSTALL_FULLDIR})
4040
# Configure the codata Python module
41-
configure_file(${PROJECT_SOURCE_DIR}/tools/codata.py.in ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR}/codata.py @ONLY)
41+
file(COPY ${PROJECT_SOURCE_DIR}/tools/codata.py DESTINATION ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR})
4242
install(FILES ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR}/codata.py DESTINATION ${PYMOD_INSTALL_FULLDIR})
43+
# Configure the plot_cavity Python script
44+
file(COPY ${PROJECT_SOURCE_DIR}/tools/plot_cavity.py DESTINATION ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR})
45+
install(FILES ${PROJECT_BINARY_DIR}/${PYMOD_INSTALL_FULLDIR}/plot_cavity.py DESTINATION ${PYMOD_INSTALL_FULLDIR})
4346

4447
# Install GetKw Python bindings
4548
# If using Python 3 use py3.x-getkw.py

tests/C_host/C_host.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ int main() {
157157
test_surface_functions(
158158
output, grid_size, mep, asc_Ag, asc_B3g, asc_neq_B3g, areas);
159159

160+
pcmsolver_save_surface_function(pcm_context, mep_lbl);
161+
pcmsolver_save_surface_function(pcm_context, asc_lbl);
162+
160163
pcmsolver_write_timings(pcm_context);
161164

162165
pcmsolver_delete(pcm_context);

tools/codata.py.in renamed to tools/codata.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
#!@PYTHON_EXECUTABLE@
2-
3-
41
#
52
# PCMSolver, an API for the Polarizable Continuum Model
63
# Copyright (C) 2017 Roberto Di Remigio, Luca Frediani and collaborators.

tools/pcmsolver.py.in

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
#!@PYTHON_EXECUTABLE@
2-
3-
41
#
52
# PCMSolver, an API for the Polarizable Continuum Model
63
# Copyright (C) 2017 Roberto Di Remigio, Luca Frediani and collaborators.

tools/plot_cavity.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#
2+
# PCMSolver, an API for the Polarizable Continuum Model
3+
# Copyright (C) 2017 Roberto Di Remigio, Luca Frediani and collaborators.
4+
#
5+
# This file is part of PCMSolver.
6+
#
7+
# PCMSolver is free software: you can redistribute it and/or modify
8+
# it under the terms of the GNU Lesser General Public License as published by
9+
# the Free Software Foundation, either version 3 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# PCMSolver is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU Lesser General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU Lesser General Public License
18+
# along with PCMSolver. If not, see <http://www.gnu.org/licenses/>.
19+
#
20+
# For information on the complete list of contributors to the
21+
# PCMSolver API, see: <http://pcmsolver.readthedocs.io/>
22+
#
23+
24+
# -*- python -*-
25+
# -*- coding: utf-8 -*-
26+
# vim:filetype=python:
27+
28+
# Written by Roberto Di Remigio <[email protected]>
29+
# University of Tromso, 2017
30+
31+
"""
32+
Plot molecular cavity from a compressed NumPy format file.
33+
Color map the finite elements according to a surface function, saved
34+
to NumPy format file.
35+
"""
36+
37+
import os
38+
import sys
39+
sys.path.append(os.path.dirname(__file__))
40+
41+
try:
42+
import docopt
43+
except:
44+
sys.path.append('cmake/lib/docopt')
45+
import docopt
46+
47+
import numpy as np
48+
import matplotlib as mpl
49+
import matplotlib.pyplot as plt
50+
from matplotlib.colors import Normalize
51+
from matplotlib import cm
52+
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
53+
54+
options = """
55+
Usage:
56+
./plot_cavity.py <cavity_npz> [--map-by <npy>]
57+
./plot_cavity.py (-h | --help)
58+
59+
Options:
60+
<cavity_npz> Compressed NumPy file with cavity specifications.
61+
--map-by <npy> NumPy format file with surface function to color-map finite elements.
62+
-h --help Show this screen.
63+
"""
64+
65+
def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name='shiftedcmap'):
66+
'''
67+
Function to offset the "center" of a colormap. Useful for
68+
data with a negative min and positive max and you want the
69+
middle of the colormap's dynamic range to be at zero
70+
71+
Input
72+
-----
73+
cmap : The matplotlib colormap to be altered
74+
start : Offset from lowest point in the colormap's range.
75+
Defaults to 0.0 (no lower ofset). Should be between
76+
0.0 and `midpoint`.
77+
midpoint : The new center of the colormap. Defaults to
78+
0.5 (no shift). Should be between 0.0 and 1.0. In
79+
general, this should be 1 - vmax/(vmax + abs(vmin))
80+
For example if your data range from -15.0 to +5.0 and
81+
you want the center of the colormap at 0.0, `midpoint`
82+
should be set to 1 - 5/(5 + 15)) or 0.75
83+
stop : Offset from highets point in the colormap's range.
84+
Defaults to 1.0 (no upper ofset). Should be between
85+
`midpoint` and 1.0.
86+
'''
87+
cdict = {
88+
'red': [],
89+
'green': [],
90+
'blue': [],
91+
'alpha': []
92+
}
93+
94+
# regular index to compute the colors
95+
reg_index = np.linspace(start, stop, 257)
96+
97+
# shifted index to match the data
98+
shift_index = np.hstack([
99+
np.linspace(0.0, midpoint, 128, endpoint=False),
100+
np.linspace(midpoint, 1.0, 129, endpoint=True)
101+
])
102+
103+
for ri, si in zip(reg_index, shift_index):
104+
r, g, b, a = cmap(ri)
105+
106+
cdict['red'].append((si, r, r))
107+
cdict['green'].append((si, g, g))
108+
cdict['blue'].append((si, b, b))
109+
cdict['alpha'].append((si, a, a))
110+
111+
newcmap = mpl.colors.LinearSegmentedColormap(name, cdict)
112+
plt.register_cmap(cmap=newcmap)
113+
114+
return newcmap
115+
116+
117+
def plot(cavity_npz, surf_func_npy=None):
118+
fig = plt.figure()
119+
ax = fig.add_subplot(111, projection='3d')
120+
121+
cavity = np.load(cavity_npz)
122+
123+
nElements = cavity['elements']
124+
centroids = cavity['centers']
125+
126+
# Plot collocation points
127+
ax.scatter(centroids[0, :], centroids[1,:], centroids[2,:], c='black', alpha=0.5)
128+
129+
# Generate color mapping
130+
colors = (.5, .1, .3, 0.3)
131+
if surf_func_npy:
132+
surf_func = np.load(surf_func_npy)
133+
shifted_cmap = shiftedColorMap(cm.coolwarm, midpoint=0.75, name='shifted')
134+
mappable = cm.ScalarMappable(cmap=shifted_cmap)
135+
mappable.set_array(surf_func.flatten())
136+
plt.colorbar(mappable)
137+
# Provide colors for Poly3DCollection
138+
colors = mappable.to_rgba(surf_func.flatten())
139+
# Generate list of vertices
140+
vertices = [zip(cavity['vertices_' + str(i)][0, :], cavity['vertices_' + str(i)][1, :], cavity['vertices_' + str(i)][2, :]) for i in range(nElements)]
141+
elements = Poly3DCollection(vertices, facecolors=colors)
142+
ax.add_collection3d(elements)
143+
ax.set_axis_off()
144+
plt.show()
145+
146+
147+
def main():
148+
try:
149+
arguments = docopt.docopt(options, argv=None)
150+
except docopt.DocoptExit:
151+
sys.stderr.write('ERROR: bad input to %s\n' % sys.argv[0])
152+
sys.stderr.write(options)
153+
sys.exit(-1)
154+
cavity_npz = arguments['<cavity_npz>']
155+
surf_func_npy = arguments['--map-by']
156+
plot(cavity_npz, surf_func_npy)
157+
158+
159+
if __name__ == '__main__':
160+
main()

0 commit comments

Comments
 (0)