htpolynet 2.0.0__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.
- htpolynet/__init__.py +2 -0
- htpolynet/analysis/__init__.py +0 -0
- htpolynet/analysis/analyze.py +256 -0
- htpolynet/analysis/plot.py +588 -0
- htpolynet/analysis/postsim.py +444 -0
- htpolynet/analysis/utils.py +399 -0
- htpolynet/cli.py +488 -0
- htpolynet/core/__init__.py +0 -0
- htpolynet/core/bondtemplate.py +112 -0
- htpolynet/core/configuration.py +83 -0
- htpolynet/core/coordinates.py +644 -0
- htpolynet/core/molecule.py +998 -0
- htpolynet/core/projectfilesystem.py +616 -0
- htpolynet/core/runtime.py +774 -0
- htpolynet/core/topocoord.py +2044 -0
- htpolynet/core/topology.py +1138 -0
- htpolynet/cure/__init__.py +0 -0
- htpolynet/cure/chain.py +135 -0
- htpolynet/cure/curecontroller.py +746 -0
- htpolynet/cure/expandreactions.py +294 -0
- htpolynet/cure/reaction.py +280 -0
- htpolynet/external/__init__.py +0 -0
- htpolynet/external/ambertools.py +97 -0
- htpolynet/external/command.py +73 -0
- htpolynet/external/gromacs.py +308 -0
- htpolynet/external/slurm.py +111 -0
- htpolynet/external/software.py +183 -0
- htpolynet/geometry/__init__.py +0 -0
- htpolynet/geometry/bondlist.py +205 -0
- htpolynet/geometry/lattice.py +32 -0
- htpolynet/geometry/linkcell.py +210 -0
- htpolynet/geometry/matrix4.py +110 -0
- htpolynet/geometry/ring.py +274 -0
- htpolynet/io/__init__.py +6 -0
- htpolynet/io/gro.py +195 -0
- htpolynet/io/mol2.py +210 -0
- htpolynet/io/pdb.py +157 -0
- htpolynet/resources/README.md +20 -0
- htpolynet/resources/__init__.py +0 -0
- htpolynet/resources/cfg/DGE-PAC-hi.yaml +115 -0
- htpolynet/resources/cfg/DGE-PAC-lo.yaml +115 -0
- htpolynet/resources/cfg/FDE-DFDA-hi.yaml +116 -0
- htpolynet/resources/cfg/FDE-DFDA-lo.yaml +116 -0
- htpolynet/resources/cfg/GMASTY-hi.yaml +158 -0
- htpolynet/resources/cfg/GMASTY-lo.yaml +158 -0
- htpolynet/resources/cfg/README.md +1 -0
- htpolynet/resources/cfg/STY.yaml +23 -0
- htpolynet/resources/cfg/pMSTY-hi.yaml +101 -0
- htpolynet/resources/cfg/pMSTY-lo.yaml +101 -0
- htpolynet/resources/cfg/pSTY-hi.yaml +101 -0
- htpolynet/resources/cfg/pSTY-lo.yaml +101 -0
- htpolynet/resources/example_depot/0-liquid-styrene.sh +134 -0
- htpolynet/resources/example_depot/0-liquid-styrene.tgz +0 -0
- htpolynet/resources/example_depot/1-polystyrene.sh +202 -0
- htpolynet/resources/example_depot/1-polystyrene.tgz +0 -0
- htpolynet/resources/example_depot/2-bisgma-styrene-thermoset.sh +353 -0
- htpolynet/resources/example_depot/2-bisgma-styrene-thermoset.tgz +0 -0
- htpolynet/resources/example_depot/3-pacm-dgeba-epoxy-thermoset.sh +242 -0
- htpolynet/resources/example_depot/3-pacm-dgeba-epoxy-thermoset.tgz +0 -0
- htpolynet/resources/example_depot/4-dfda-fde-epoxy-thermoset.sh +216 -0
- htpolynet/resources/example_depot/4-dfda-fde-epoxy-thermoset.tgz +0 -0
- htpolynet/resources/mdp/README.md +1 -0
- htpolynet/resources/mdp/drag-min.mdp +13 -0
- htpolynet/resources/mdp/drag-npt.mdp +36 -0
- htpolynet/resources/mdp/drag-nvt.mdp +31 -0
- htpolynet/resources/mdp/min.mdp +14 -0
- htpolynet/resources/mdp/npt.mdp +37 -0
- htpolynet/resources/mdp/nvt.mdp +29 -0
- htpolynet/resources/mdp/relax-min.mdp +13 -0
- htpolynet/resources/mdp/relax-npt.mdp +37 -0
- htpolynet/resources/mdp/relax-nvt.mdp +31 -0
- htpolynet/resources/mdp/single-molecule-min.mdp +12 -0
- htpolynet/resources/mdp/single-molecule-nvt.mdp +25 -0
- htpolynet/resources/molecules/inputs/DFA.pdb +62 -0
- htpolynet/resources/molecules/inputs/DGE.mol2 +115 -0
- htpolynet/resources/molecules/inputs/EMB.mol2 +50 -0
- htpolynet/resources/molecules/inputs/FDE.pdb +72 -0
- htpolynet/resources/molecules/inputs/GMA.mol2 +163 -0
- htpolynet/resources/molecules/inputs/PAC.mol2 +91 -0
- htpolynet/resources/molecules/inputs/STY.mol2 +44 -0
- htpolynet/resources/molecules/make-monomers.sh +64 -0
- htpolynet/resources/molecules/pics/DFA.png +0 -0
- htpolynet/resources/molecules/pics/DGE.png +0 -0
- htpolynet/resources/molecules/pics/EMB.png +0 -0
- htpolynet/resources/molecules/pics/FDE.png +0 -0
- htpolynet/resources/molecules/pics/GMA.png +0 -0
- htpolynet/resources/molecules/pics/PAC.png +0 -0
- htpolynet/resources/molecules/pics/STY.png +0 -0
- htpolynet/resources/molecules/sample-inputs/DFA.pdb +62 -0
- htpolynet/resources/molecules/sample-inputs/DGE.mol2 +115 -0
- htpolynet/resources/molecules/sample-inputs/EMB.mol2 +50 -0
- htpolynet/resources/molecules/sample-inputs/FDE.pdb +72 -0
- htpolynet/resources/molecules/sample-inputs/GMA.mol2 +163 -0
- htpolynet/resources/molecules/sample-inputs/PAC.mol2 +91 -0
- htpolynet/resources/molecules/sample-inputs/STY.mol2 +44 -0
- htpolynet/resources/tcl/readbonds.tcl +32 -0
- htpolynet/resources/tcl/readgrx.tcl +46 -0
- htpolynet/resources/tcl/render.tcl +74 -0
- htpolynet/utils/__init__.py +0 -0
- htpolynet/utils/banner.py +34 -0
- htpolynet/utils/checkpoint.py +85 -0
- htpolynet/utils/dataframetools.py +92 -0
- htpolynet/utils/inputcheck.py +49 -0
- htpolynet/utils/logsetup.py +44 -0
- htpolynet/utils/stringthings.py +39 -0
- htpolynet-2.0.0.dist-info/METADATA +78 -0
- htpolynet-2.0.0.dist-info/RECORD +110 -0
- htpolynet-2.0.0.dist-info/WHEEL +4 -0
- htpolynet-2.0.0.dist-info/entry_points.txt +2 -0
- htpolynet-2.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
"""Provides plotting functionality.
|
|
2
|
+
|
|
3
|
+
Author: Cameron F. Abrams <cfa22@drexel.edu>
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
import matplotlib.cm as cm
|
|
12
|
+
import matplotlib.pyplot as plt
|
|
13
|
+
import networkx as nx
|
|
14
|
+
import pandas as pd
|
|
15
|
+
|
|
16
|
+
from ..analysis.utils import *
|
|
17
|
+
from ..external.gromacs import *
|
|
18
|
+
from ..utils.logsetup import setup_logging
|
|
19
|
+
|
|
20
|
+
logger=logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
# prevents "RuntimeError: main thread is not in main loop" tk bug
|
|
23
|
+
plt.switch_backend('agg')
|
|
24
|
+
|
|
25
|
+
def scatter(df,xcolumn,columns=[],outfile='plot.png',**kwargs):
|
|
26
|
+
"""Generic scatter plot generator.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
df (pd.DataFrame): dataframe containing data
|
|
30
|
+
xcolumn (str): name of column holding x-data
|
|
31
|
+
columns (list): list of y-value columns to be plotted vs. x, defaults to []
|
|
32
|
+
outfile (str): name of output image file, defaults to 'plot.png'
|
|
33
|
+
"""
|
|
34
|
+
logging.disable(logging.DEBUG)
|
|
35
|
+
cmapname=kwargs.get('colormap','plasma')
|
|
36
|
+
size=kwargs.get('size',(8,6))
|
|
37
|
+
yunits=kwargs.get('yunits',None)
|
|
38
|
+
cmap=cm.get_cmap(cmapname)
|
|
39
|
+
fig,ax=plt.subplots(1,1,figsize=size)
|
|
40
|
+
ax.set_xlabel(xcolumn)
|
|
41
|
+
for n in columns:
|
|
42
|
+
ax.scatter(df[xcolumn],df[n],label=n)
|
|
43
|
+
plt.legend()
|
|
44
|
+
plt.savefig(outfile)
|
|
45
|
+
plt.close(fig)
|
|
46
|
+
logging.disable(logging.NOTSET)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def trace(qty,edrs,outfile='plot.png',**kwargs):
|
|
50
|
+
"""Generates a plot of the energy-like quantity named by 'qty' vs time by reading data from the list of edr files named in 'edrs'.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
qty (str): name of energy-like quantity; must conform to menu generated by 'gmx energy'
|
|
54
|
+
edrs (list): list of names of edr files to scan, in order
|
|
55
|
+
outfile (str): name of output image file, defaults to 'plot.png'
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
list: the list of average values
|
|
59
|
+
"""
|
|
60
|
+
# disable debug-level logging and above since matplotlib has a lot of debug statements
|
|
61
|
+
logging.disable(logging.DEBUG)
|
|
62
|
+
df=pd.DataFrame()
|
|
63
|
+
cmapname=kwargs.get('colormap','plasma')
|
|
64
|
+
size=kwargs.get('size',(8,6))
|
|
65
|
+
yunits=kwargs.get('yunits',None)
|
|
66
|
+
avgafter=kwargs.get('avgafter',0)
|
|
67
|
+
cmap=cm.get_cmap(cmapname)
|
|
68
|
+
xshift=0.0
|
|
69
|
+
chkpt=[]
|
|
70
|
+
for edr in edrs:
|
|
71
|
+
if not df.empty:
|
|
72
|
+
xshift=df.tail(1).iloc[0,0]
|
|
73
|
+
data=gmx_energy_trace(edr,[qty],xshift=xshift)
|
|
74
|
+
lastchkpt=0
|
|
75
|
+
if len(chkpt)>0:
|
|
76
|
+
lastchkpt=chkpt[-1]
|
|
77
|
+
chkpt.append(data.shape[0]+lastchkpt)
|
|
78
|
+
df=pd.concat((df,data),ignore_index=True)
|
|
79
|
+
fig,ax=plt.subplots(1,1,figsize=size)
|
|
80
|
+
nseg=len(chkpt)
|
|
81
|
+
beg=0
|
|
82
|
+
avg=[]
|
|
83
|
+
for c in df.columns[1:]:
|
|
84
|
+
for seg in range(nseg):
|
|
85
|
+
ax.plot(df.iloc[beg:chkpt[seg],0],df[c].iloc[beg:chkpt[seg]],label=(c if seg==0 else None),color=cmap(seg/nseg))
|
|
86
|
+
beg=chkpt[seg]
|
|
87
|
+
if 'avgafter' in kwargs:
|
|
88
|
+
if avgafter>0:
|
|
89
|
+
pass
|
|
90
|
+
else:
|
|
91
|
+
avgafter=df['time(ps)'].iloc[-1]/2
|
|
92
|
+
sdf=df[df['time(ps)']>avgafter]
|
|
93
|
+
avg.append(sdf[c].mean())
|
|
94
|
+
ax.plot(df.iloc[:,0],[avg]*df.shape[0],'k-',alpha=0.3,label=f'{avg:0.2f}')
|
|
95
|
+
else:
|
|
96
|
+
avg.append(df[c].mean())
|
|
97
|
+
if not yunits:
|
|
98
|
+
plt.ylabel(qty)
|
|
99
|
+
else:
|
|
100
|
+
plt.ylabel(f'{qty} ({yunits})')
|
|
101
|
+
plt.xlabel('time(ps)')
|
|
102
|
+
plt.legend()
|
|
103
|
+
plt.savefig(outfile)
|
|
104
|
+
plt.close(fig)
|
|
105
|
+
# re-establish previous logging level
|
|
106
|
+
logging.disable(logging.NOTSET)
|
|
107
|
+
return avg
|
|
108
|
+
|
|
109
|
+
def multi_trace(dfL,xnames,ynames,labels=[],xlabel='time [ps]',ylabel='',outfile='plot.png',**kwargs):
|
|
110
|
+
"""Generates a plot of each y vs x in df.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
dfL (list of pandas.DataFrame): list of dataframes, one per trace
|
|
114
|
+
xnames (list): list of x-column names
|
|
115
|
+
ynames (list): list of y-column names, parallel to xnames
|
|
116
|
+
outfile (str): name of output image file, defaults to 'plot.png'
|
|
117
|
+
"""
|
|
118
|
+
# disable debug-level logging and above since matplotlib has a lot of debug statements
|
|
119
|
+
default_units={'Temperature':'K','Pressure':'bar','Density':'kg/m^3','Potential':'kJ/mol'}
|
|
120
|
+
units=kwargs.get('units',default_units)
|
|
121
|
+
logging.disable(logging.DEBUG)
|
|
122
|
+
size=kwargs.get('size',(16,4))
|
|
123
|
+
legend=kwargs.get('legend',True)
|
|
124
|
+
fig,ax=plt.subplots(1,1,figsize=size)
|
|
125
|
+
plt.xlabel(xlabel)
|
|
126
|
+
plt.ylabel(ylabel)
|
|
127
|
+
cmapname=kwargs.get('colormap','plasma')
|
|
128
|
+
cmap=cm.get_cmap(cmapname)
|
|
129
|
+
|
|
130
|
+
ndatasets=len(xnames)
|
|
131
|
+
assert ndatasets==len(ynames)
|
|
132
|
+
|
|
133
|
+
for i,(df,x,y,l) in enumerate(zip(dfL,xnames,ynames,labels)):
|
|
134
|
+
ax.plot(df[x],df[y],label=l,color=cmap(i/ndatasets))
|
|
135
|
+
|
|
136
|
+
if legend:
|
|
137
|
+
plt.legend()
|
|
138
|
+
plt.savefig(outfile)
|
|
139
|
+
plt.close(fig)
|
|
140
|
+
# re-establish previous logging level
|
|
141
|
+
logging.disable(logging.NOTSET)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def global_trace(df,names,outfile='plot.png',transition_times=[],markers=[],interval_labels=[],y2names=[],**kwargs):
|
|
145
|
+
"""Generates custom-formatted multiplots of energy-like quantities named in 'names' in the input dataframe df.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
df (pd.DataFrame): pandas dataframe containing all data
|
|
149
|
+
names (list): list of quantity names (Density, Temperature, etc)
|
|
150
|
+
outfile (str): name of output image file, defaults to 'plot.png'
|
|
151
|
+
transition_times (list): time values at which vertical lines are drawn, defaults to []
|
|
152
|
+
markers (list): time values at which transitions are marked, defaults to []
|
|
153
|
+
interval_labels (list): list of labels of intervals defined by markers, defaults to []
|
|
154
|
+
y2names (list): names of quantities to be plotted on a secondary y axis, defaults to []
|
|
155
|
+
"""
|
|
156
|
+
# disable debug-level logging and above since matplotlib has a lot of debug statements
|
|
157
|
+
default_units={'Temperature':'K','Pressure':'bar','Density':'kg/m^3','Potential':'kJ/mol'}
|
|
158
|
+
units=kwargs.get('units',default_units)
|
|
159
|
+
logging.disable(logging.DEBUG)
|
|
160
|
+
size=kwargs.get('size',(16,4*len(names)))
|
|
161
|
+
legend=kwargs.get('legend',False)
|
|
162
|
+
fig,ax=plt.subplots(len(names),1,figsize=size)
|
|
163
|
+
plt.xlabel('time(ps)')
|
|
164
|
+
cmapname=kwargs.get('colormap','plasma')
|
|
165
|
+
# yunits=kwargs.get('yunits',None)
|
|
166
|
+
cmap=cm.get_cmap(cmapname)
|
|
167
|
+
# print(f'in global_trace:\n{df.head().to_string()}')
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
interval_times=[]
|
|
171
|
+
if interval_labels:
|
|
172
|
+
for i in range(1,len(transition_times)):
|
|
173
|
+
interval_times.append((transition_times[i]+transition_times[i-1])/2)
|
|
174
|
+
for l,t in zip(interval_labels,interval_times):
|
|
175
|
+
logger.info(f'{t} {l}')
|
|
176
|
+
assert len(interval_labels)==len(interval_times)
|
|
177
|
+
L,R=-1,-1
|
|
178
|
+
if len(markers)>1:
|
|
179
|
+
L,R=markers[0],markers[-1]
|
|
180
|
+
in_tt=[x for x in transition_times if L<x<R]
|
|
181
|
+
marked_df=df[(df['time(ps)']>L)&(df['time(ps)']<R)]
|
|
182
|
+
fig,ax=plt.subplots(len(names)*2,1,figsize=size)
|
|
183
|
+
for i,colname in enumerate(names):
|
|
184
|
+
out_ax=ax[0] if len(names)==1 else ax[i*2]
|
|
185
|
+
in_ax=ax[1] if len(names)==1 else ax[i*2+1]
|
|
186
|
+
out_ax.plot(df['time(ps)'],df[colname],label=colname)
|
|
187
|
+
ylabel=colname
|
|
188
|
+
if ylabel in units: ylabel+=f' ({units[ylabel]})'
|
|
189
|
+
out_ax.set_ylabel(ylabel)
|
|
190
|
+
out_ax.set_xlabel('time(ps)')
|
|
191
|
+
if len(y2names)>i:
|
|
192
|
+
out_ax2=out_ax.twinx()
|
|
193
|
+
out_ax2.plot(df['time(ps)'],df[y2names[i]],label=y2names[i],color='black')
|
|
194
|
+
ylabel=y2names[i]
|
|
195
|
+
if ylabel in units: ylabel+=f' ({units[ylabel]})'
|
|
196
|
+
out_ax2.set_ylabel(ylabel)
|
|
197
|
+
out_ax2.set_xlabel('time(ps)')
|
|
198
|
+
if len(transition_times)>0:
|
|
199
|
+
colors=[cmap(i/len(transition_times)) for i in range(len(transition_times))]
|
|
200
|
+
ylim=out_ax.get_ylim()
|
|
201
|
+
out_ax.vlines(transition_times,ylim[0],ylim[1],color=colors,linewidth=0.75,alpha=0.5)
|
|
202
|
+
# for x,l in zip(interval_times,interval_labels):
|
|
203
|
+
# out_ax.text(x,0.9*ylim[1],l,fontsize=8)
|
|
204
|
+
in_ax.plot(marked_df['time(ps)'],marked_df[colname],label=colname)
|
|
205
|
+
ylabel=colname
|
|
206
|
+
if ylabel in units: ylabel+=f' ({units[ylabel]})'
|
|
207
|
+
in_ax.set_xlabel('time(ps)')
|
|
208
|
+
in_ax.set_yabel(ylabel)
|
|
209
|
+
if len(y2names)>i:
|
|
210
|
+
in_ax2=in_ax.twinx()
|
|
211
|
+
in_ax2.plot(marked_df['time(ps)'],marked_df[y2names[i]],label=y2names[i],color='black')
|
|
212
|
+
ylabel=y2names[i]
|
|
213
|
+
if ylabel in units: ylabel+=f' ({units[ylabel]})'
|
|
214
|
+
in_ax2.set_ylabel(ylabel)
|
|
215
|
+
in_ax2.set_xlabel('time(ps)')
|
|
216
|
+
if len(transition_times)>0:
|
|
217
|
+
colors=[cmap(i/len(transition_times)) for i in range(len(transition_times))]
|
|
218
|
+
ylim=in_ax.get_ylim()
|
|
219
|
+
in_ax.vlines(in_tt,ylim[0],ylim[1],color=colors,linewidth=0.75,alpha=0.5)
|
|
220
|
+
# for x,l in zip(interval_times,interval_labels):
|
|
221
|
+
# if L<x<R:
|
|
222
|
+
# out_ax.text(x,0.9*ylim[1],l,fontsize=8)
|
|
223
|
+
else:
|
|
224
|
+
fig,ax=plt.subplots(len(names),1,figsize=size)
|
|
225
|
+
for i,colname in enumerate(names):
|
|
226
|
+
the_ax=ax if len(names)==1 else ax[i]
|
|
227
|
+
the_ax.plot(df['time(ps)'],df[colname],label=colname)
|
|
228
|
+
ylabel=colname
|
|
229
|
+
if ylabel in units: ylabel+=f' ({units[ylabel]})'
|
|
230
|
+
the_ax.set_ylabel(ylabel)
|
|
231
|
+
the_ax.set_xlabel('time(ps)')
|
|
232
|
+
if len(y2names)>i:
|
|
233
|
+
the_ax2=the_ax.twinx()
|
|
234
|
+
the_ax2.plot(df['time(ps)'],df[y2names[i]],label=y2names[i],color='black')
|
|
235
|
+
ylabel=y2names[i]
|
|
236
|
+
if ylabel in units: ylabel+=f' ({units[ylabel]})'
|
|
237
|
+
the_ax2.set_ylabel(ylabel)
|
|
238
|
+
if len(transition_times)>0:
|
|
239
|
+
colors=[cmap(i/len(transition_times)) for i in range(len(transition_times))]
|
|
240
|
+
ylim=the_ax.get_ylim()
|
|
241
|
+
the_ax.vlines(transition_times,ylim[0],ylim[1],color=colors,linewidth=0.5,alpha=0.5)
|
|
242
|
+
|
|
243
|
+
# plt.xlabel('time(ps)')
|
|
244
|
+
if legend:
|
|
245
|
+
plt.legend()
|
|
246
|
+
plt.savefig(outfile)
|
|
247
|
+
plt.close(fig)
|
|
248
|
+
# re-establish previous logging level
|
|
249
|
+
logging.disable(logging.NOTSET)
|
|
250
|
+
|
|
251
|
+
def network_graph(G,filename,**kwargs):
|
|
252
|
+
"""Draws a custom formatted network plot from graph G.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
G (nx.Graph or nx.DiGraph): a graph from networkx
|
|
256
|
+
filename (str): name of output image filename
|
|
257
|
+
"""
|
|
258
|
+
logging.disable(logging.DEBUG)
|
|
259
|
+
arrows=kwargs.get('arrows',False)
|
|
260
|
+
figsize=kwargs.get('figsize',(32,32))
|
|
261
|
+
node_size=kwargs.get('node_size',200)
|
|
262
|
+
with_labels=kwargs.get('with_labels',False)
|
|
263
|
+
cmap=cm.get_cmap('seismic')
|
|
264
|
+
fig,ax=plt.subplots(1,1,figsize=figsize)
|
|
265
|
+
ax.axis('off')
|
|
266
|
+
molnames=list(set([n.get('molecule_name','anonymous') for k,n in G.nodes.items()]))
|
|
267
|
+
nmolname=len(molnames)
|
|
268
|
+
cx=[]
|
|
269
|
+
for n in G.nodes.values():
|
|
270
|
+
idx=molnames.index(n.get('molecule_name','anonymous'))
|
|
271
|
+
cx.append((float(idx)+0.25)/(nmolname+1))
|
|
272
|
+
nx.draw_networkx(G,ax=ax,arrows=arrows,node_size=node_size,node_color=cx,cmap=cmap,with_labels=with_labels)
|
|
273
|
+
plt.savefig(filename)
|
|
274
|
+
plt.close(fig)
|
|
275
|
+
logging.disable(logging.NOTSET)
|
|
276
|
+
|
|
277
|
+
# below are representive diagnostic output lines to establish extraction patterns
|
|
278
|
+
_template_1='2022-08-11 17:40:36,969 HTPolyNet.runtime.my_logger INFO> ********* Connect-Update-Relax-Equilibrate (CURE) begins **********'
|
|
279
|
+
_template_1_token_idx=[2,3,5,7]
|
|
280
|
+
_template_2='2022-09-03 19:32:46,830 HTPolyNet.curecontroller.do_iter INFO> Iteration 1 current conversion 0.283 or 1082 bonds'
|
|
281
|
+
_template_2_token_idx=[2,3,6,7]
|
|
282
|
+
_template_2_data_idx={'iter':(int,5),'conv':(float,8),'nbonds':(int,10)}
|
|
283
|
+
|
|
284
|
+
def _token_match(l,template,pat_idx):
|
|
285
|
+
"""Returns True if tokens indexed by pat_idx in the space-split l and template match.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
l (str): probe string
|
|
289
|
+
template (str): template string
|
|
290
|
+
pat_idx (list): list of token indices
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
bool: True if tokens in l match those in template
|
|
294
|
+
"""
|
|
295
|
+
if len(l.split())!=len(template.split()): return
|
|
296
|
+
return all([l.split()[t]==template.split()[t] for t in pat_idx])
|
|
297
|
+
|
|
298
|
+
def _parse_data(dat,l,idx_dict):
|
|
299
|
+
"""Parses data in a matching line.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
dat (pd.DataFrame): pandas dataframe
|
|
303
|
+
l (str): line
|
|
304
|
+
idx_dict (dict): dictionary of column-name:(type-converter,token-index)
|
|
305
|
+
"""
|
|
306
|
+
tok=l.split()
|
|
307
|
+
for k,v in idx_dict.items():
|
|
308
|
+
conv,s=v
|
|
309
|
+
dat[k].append(conv(tok[s]))
|
|
310
|
+
|
|
311
|
+
def diagnostics_graphs(logfiles,filename,**kwargs):
|
|
312
|
+
"""Extracts selected data from the diagnostic output and generates plots.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
logfiles (list): list of names of diagnostic log files to treat in parallel
|
|
316
|
+
filename (str): name of output image file
|
|
317
|
+
"""
|
|
318
|
+
xmax=kwargs.get('xmax',-1)
|
|
319
|
+
figsize=kwargs.get('figsize',(12,6))
|
|
320
|
+
logging.disable(logging.DEBUG)
|
|
321
|
+
df:dict[pd.DataFrame]={}
|
|
322
|
+
for logfile in logfiles:
|
|
323
|
+
bn,ex=os.path.splitext(logfile)
|
|
324
|
+
with open(logfile,'r') as f:
|
|
325
|
+
lines=f.read().split('\n')
|
|
326
|
+
logger.info(f'read {len(lines)} lines from {logfile}')
|
|
327
|
+
data={}
|
|
328
|
+
data['time']=[]
|
|
329
|
+
data['iter']=[]
|
|
330
|
+
data['conv']=[]
|
|
331
|
+
data['nbonds']=[]
|
|
332
|
+
counter=0
|
|
333
|
+
for l in lines:
|
|
334
|
+
if _token_match(l,_template_1,_template_1_token_idx):
|
|
335
|
+
# print('you should only see this once')
|
|
336
|
+
counter+=1
|
|
337
|
+
assert not counter>1
|
|
338
|
+
data['time'].append(datetime.strptime(' '.join(l.split()[0:2]),'%Y-%m-%d %H:%M:%S,%f'))
|
|
339
|
+
data['iter'].append(0)
|
|
340
|
+
data['conv'].append(0.0)
|
|
341
|
+
data['nbonds'].append(0)
|
|
342
|
+
elif _token_match(l,_template_2,_template_2_token_idx):
|
|
343
|
+
data['time'].append(datetime.strptime(' '.join(l.split()[0:2]),'%Y-%m-%d %H:%M:%S,%f'))
|
|
344
|
+
# print('data tok',f'{l.split()}')
|
|
345
|
+
_parse_data(data,l,_template_2_data_idx)
|
|
346
|
+
# print('data',f'{data}')
|
|
347
|
+
df[logfile]=pd.DataFrame(data)
|
|
348
|
+
time_idx=list(df[logfile].columns).index('time')
|
|
349
|
+
df[logfile]['elapsed']=(df[logfile]['time']-df[logfile].iloc[0,time_idx]).astype(int)/1.e9/3600.0
|
|
350
|
+
df[logfile].to_csv(f'{bn}.csv',index=False,sep=' ',header=True)
|
|
351
|
+
fig,ax=plt.subplots(1,2,sharex=True,figsize=figsize)
|
|
352
|
+
ax[0].set_ylim([0,1])
|
|
353
|
+
ax[0].set_xlabel('runtime (h)')
|
|
354
|
+
ax[0].set_ylabel('conversion')
|
|
355
|
+
ax[1].set_xlabel('runtime (h)')
|
|
356
|
+
ax[1].set_ylabel('iteration')
|
|
357
|
+
if xmax>-1:
|
|
358
|
+
ax[0].set_xlim([0,xmax])
|
|
359
|
+
for logfile in logfiles:
|
|
360
|
+
ax[0].plot(df[logfile]['elapsed'],df[logfile]['conv'],label=logfile)
|
|
361
|
+
ax[1].plot(df[logfile]['elapsed'],df[logfile].index+1,label=logfile)
|
|
362
|
+
plt.legend()
|
|
363
|
+
plt.savefig(filename)
|
|
364
|
+
plt.close(fig)
|
|
365
|
+
logging.disable(logging.NOTSET)
|
|
366
|
+
|
|
367
|
+
def init_molecule_graph(proj_dir):
|
|
368
|
+
"""Creates and initializes an inter-molecular graph to show network connectivity.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
proj_dir (str): name of project directory
|
|
372
|
+
|
|
373
|
+
Returns:
|
|
374
|
+
networkx.Graph: a nodes-only Graph enumerating all molecules; this will be further processed elsewhere to add connectivity information
|
|
375
|
+
"""
|
|
376
|
+
gro=os.path.join(proj_dir,'systems/init/init.gro')
|
|
377
|
+
top=os.path.join(proj_dir,'systems/init/init.top')
|
|
378
|
+
grx=os.path.join(proj_dir,'systems/init/init.grx')
|
|
379
|
+
TC=TopoCoord(grofilename=gro,topfilename=top,grxfilename=grx)
|
|
380
|
+
G=nx.Graph()
|
|
381
|
+
adf=TC.Coordinates.A
|
|
382
|
+
mm=set(zip(adf['molecule'],adf['molecule_name']))
|
|
383
|
+
for mx,mn in mm:
|
|
384
|
+
G.add_node(mx,molecule_name=mn)
|
|
385
|
+
return G
|
|
386
|
+
|
|
387
|
+
def plots(args):
|
|
388
|
+
"""Handles the plots subcommand.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
args (argparse.Namespace): command-line arguments
|
|
392
|
+
"""
|
|
393
|
+
setup_logging(args.loglevel, no_banner=args.no_banner)
|
|
394
|
+
|
|
395
|
+
if args.source=='build':
|
|
396
|
+
build_plots(args)
|
|
397
|
+
elif args.source=='diag':
|
|
398
|
+
diag_plots(args)
|
|
399
|
+
elif args.source=='post':
|
|
400
|
+
post_plots(args)
|
|
401
|
+
else:
|
|
402
|
+
logger.error(f'Source {args.source} is not recognized.')
|
|
403
|
+
|
|
404
|
+
def diag_plots(args):
|
|
405
|
+
diags=args.diags
|
|
406
|
+
plotfile=args.plotfile
|
|
407
|
+
if not plotfile:
|
|
408
|
+
plotfile='cure_info.png'
|
|
409
|
+
if len(diags)>0:
|
|
410
|
+
diagnostics_graphs(diags,plotfile)
|
|
411
|
+
|
|
412
|
+
def build_plots(args):
|
|
413
|
+
GmxNames={'t':'Temperature','d':'Density','p':'Potential'}
|
|
414
|
+
plot_types=args.buildplot
|
|
415
|
+
trace_types=[]
|
|
416
|
+
if 't' in plot_types:
|
|
417
|
+
trace_types=[GmxNames[i] for i in args.traces]
|
|
418
|
+
for p in args.proj:
|
|
419
|
+
if trace_types:
|
|
420
|
+
df,transition_times,cure_markers,interval_labels=density_evolution(p)
|
|
421
|
+
global_trace(df,trace_types,os.path.join(p,'buildtraces.png'),transition_times=transition_times,markers=[],interval_labels=interval_labels,y2names=['nbonds','nbonds'],legend=True)
|
|
422
|
+
df.to_csv(os.path.join(p,'buildtraces.csv'),index=False,header=True,float_format='{:.3f}'.format)
|
|
423
|
+
if any([x in plot_types for x in 'gnc']):
|
|
424
|
+
G=init_molecule_graph(p)
|
|
425
|
+
n=1
|
|
426
|
+
while os.path.exists(os.path.join(p,f'systems/iter-{n}/2-cure_update-bonds.csv')):
|
|
427
|
+
logger.info(f'iter-{n}/2-cure_update-bonds.csv')
|
|
428
|
+
g=graph_from_bondsfile(os.path.join(p,f'systems/iter-{n}/2-cure_update-bonds.csv'))
|
|
429
|
+
G=nx.compose(G,g)
|
|
430
|
+
if 'g' in plot_types:
|
|
431
|
+
network_graph(G,os.path.join(p,f'plots/iter-{n}-graph.png'))
|
|
432
|
+
n+=1
|
|
433
|
+
if 'g' in plot_types: network_graph(G,os.path.join(p,'graph.png'))
|
|
434
|
+
if 'c' in plot_types:
|
|
435
|
+
clu=clusters(G)
|
|
436
|
+
clu.to_csv(os.path.join(p,'clusters.csv'),sep=' ',header=True,index=False)
|
|
437
|
+
logger.info(f'{os.path.join(p,"clusters.csv")} created.')
|
|
438
|
+
# cluster_plot(clu,os.path.join(p,"clusters.png"))
|
|
439
|
+
if 'n' in plot_types:
|
|
440
|
+
am=mwbxl(G)
|
|
441
|
+
logger.info(f'Avg homo-N between xlinks: {np.average(am["n"],weights=am["counts"]):.2f}')
|
|
442
|
+
am.to_csv(os.path.join(p,'dist_bw_xlinks.csv'),sep=' ',index=False,header=True)
|
|
443
|
+
logger.info(f'{os.path.join(p,"dist_bw_xlinks.csv")} created.')
|
|
444
|
+
# dist_bw_xlinks_plot(am,os.path.join(p,"dist_bw_xlinks.png"))
|
|
445
|
+
|
|
446
|
+
def do_tg_plots(phases,projdirs,outfile='tg.png',save_data='data.csv',n_points=[10,20]):
|
|
447
|
+
res={}
|
|
448
|
+
means={}
|
|
449
|
+
stds={}
|
|
450
|
+
rate=[]
|
|
451
|
+
Tgs=[]
|
|
452
|
+
nproj=len(projdirs)
|
|
453
|
+
for i,phase in enumerate(phases):
|
|
454
|
+
p=phase['ladder']
|
|
455
|
+
rate.append(p['deltaT']/(p['ps_per_rise']+p['ps_per_run']))
|
|
456
|
+
res[i]=[]
|
|
457
|
+
for d in projdirs:
|
|
458
|
+
df=pd.read_csv(os.path.join(d,p['subdir'],'ladder.csv'),index_col=None,header=0)
|
|
459
|
+
t0=p['warmup_ps']
|
|
460
|
+
ps_per_step=p['ps_per_rise']+p['ps_per_run']
|
|
461
|
+
final_ps=df['time(ps)'].iloc[-1]
|
|
462
|
+
curr_ps=t0
|
|
463
|
+
T=[]
|
|
464
|
+
rho=[]
|
|
465
|
+
V=[]
|
|
466
|
+
Tstd=[]
|
|
467
|
+
rhostd=[]
|
|
468
|
+
Vstd=[]
|
|
469
|
+
while curr_ps<final_ps:
|
|
470
|
+
ll=curr_ps+p['ps_per_rise']+0.5*p['ps_per_run']
|
|
471
|
+
ul=ll+0.5*p['ps_per_run']
|
|
472
|
+
tdf=df[(df['time(ps)']>=ll)&(df['time(ps)']<=ul)]
|
|
473
|
+
T.append(tdf['Temperature'].mean())
|
|
474
|
+
rho.append(tdf['Density'].mean())
|
|
475
|
+
V.append(tdf['Volume'].mean())
|
|
476
|
+
Tstd.append(tdf['Temperature'].std())
|
|
477
|
+
rhostd.append(tdf['Density'].std())
|
|
478
|
+
Vstd.append(tdf['Volume'].std())
|
|
479
|
+
curr_ps+=ps_per_step
|
|
480
|
+
|
|
481
|
+
res[i].append(pd.DataFrame({'Temperature':T,'Volume':V,'Density':rho,'Temperature-std':Tstd,'Volume-std':Vstd,'Density-std':rhostd}))
|
|
482
|
+
|
|
483
|
+
df_concat0 = pd.concat(res[i], axis=1)
|
|
484
|
+
means[i]=df_concat0.stack().groupby(level=[0,1]).mean().unstack()
|
|
485
|
+
stds[i]=df_concat0.stack().groupby(level=[0,1]).std().unstack()
|
|
486
|
+
means[i]['Volume-std']=stds[i]['Volume']
|
|
487
|
+
means[i]['Density-std']=stds[i]['Density']
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
fig,ax=plt.subplots(1,2,figsize=(10,6),sharex=True,sharey=True)
|
|
491
|
+
for i,v in enumerate(means.keys()):
|
|
492
|
+
m=means[v]
|
|
493
|
+
std=stds[v]
|
|
494
|
+
if np.isnan(np.sum(std['Density'])):
|
|
495
|
+
std['Density']=np.zeros(len(std['Density']))
|
|
496
|
+
m.sort_values('Temperature',axis=0,inplace=True)
|
|
497
|
+
ax[i].set_xlabel('Temperature [K]')
|
|
498
|
+
ax[i].set_ylabel('Density [kg/m$^3$]')
|
|
499
|
+
if nproj<2:
|
|
500
|
+
ax[i].scatter(m['Temperature'],m['Density'])
|
|
501
|
+
else:
|
|
502
|
+
ax[i].errorbar(m['Temperature'],m['Density'],std['Density'])
|
|
503
|
+
Tg,c,h=compute_tg(m['Temperature'],m['Density'],n_points=n_points)
|
|
504
|
+
Tgs.append(Tg)
|
|
505
|
+
if Tg!=-1:
|
|
506
|
+
ax[i].plot(m['Temperature'],c[0]*m['Temperature']+c[1],color='blue',alpha=0.5)
|
|
507
|
+
ax[i].plot(m['Temperature'],h[0]*m['Temperature']+h[1],color='red',alpha=0.5)
|
|
508
|
+
ax[i].scatter([Tg],[c[0]*Tg+c[1]],marker='o',color='black')
|
|
509
|
+
ax[i].text(Tg,c[0]*Tg+c[1],f' {Tg:.2f} K',verticalalignment='top')
|
|
510
|
+
means[v]['glassy-line-Density']=c[0]*m['Temperature']+c[1]
|
|
511
|
+
means[v]['rubbery-line-Density']=h[0]*m['Temperature']+h[1]
|
|
512
|
+
means[v].to_csv(f'ladder{v}-{save_data}',index=False,header=True,sep=' ')
|
|
513
|
+
logger.info(f'ladder{v}-{save_data} created.')
|
|
514
|
+
plt.savefig(outfile,bbox_inches='tight')
|
|
515
|
+
plt.close(fig)
|
|
516
|
+
hTg=Tgs[0]
|
|
517
|
+
cTg=Tgs[1]
|
|
518
|
+
logger.info(f'{outfile} created.')
|
|
519
|
+
logger.info(f'heating Tg = {hTg:.2f} K ({(hTg-273.15):.2f} C) at {rate[0]:.5f} K/ps ({rate[0]*1.e12:.3e} K/s)')
|
|
520
|
+
logger.info(f'cooling Tg = {cTg:.2f} K ({(cTg-273.15):.2f} C) at {rate[1]:.5f} K/ps ({rate[1]*1.e12:.3e} K/s)')
|
|
521
|
+
|
|
522
|
+
def do_E_plots(phases,projdirs,outfile='e.png',fit_domain=[10,200],save_data='E.csv'):
|
|
523
|
+
MPa_per_bar=1.e-1
|
|
524
|
+
# average over replicas and directions (here, phases)
|
|
525
|
+
all_stress_strains=[]
|
|
526
|
+
for p in phases:
|
|
527
|
+
params=p['deform']
|
|
528
|
+
dir=params['direction']
|
|
529
|
+
# Box-X-strain,Pres-XX-stress
|
|
530
|
+
strain_name=f'Box-{dir.upper()}-strain'
|
|
531
|
+
stress_name=f'Pres-{dir.upper()}{dir.upper()}-stress'
|
|
532
|
+
for d in projdirs:
|
|
533
|
+
df=pd.read_csv(os.path.join(d,params['subdir'],f'deform-{dir}.csv'),index_col=None,header=0)
|
|
534
|
+
all_stress_strains.append(pd.DataFrame({'strain':df[strain_name],'stress':(df[stress_name]*MPa_per_bar)}))
|
|
535
|
+
df_concat=pd.concat(all_stress_strains,axis=1)
|
|
536
|
+
mean_stress_strains=df_concat.stack().groupby(level=[0,1]).mean().unstack()
|
|
537
|
+
stds_stress_strains=df_concat.stack().groupby(level=[0,1]).std().unstack()
|
|
538
|
+
mean_stress_strains['stress-std']=stds_stress_strains['stress']
|
|
539
|
+
mean_stress_strains.to_csv(save_data,header=True,index=False,sep=' ')
|
|
540
|
+
fig,ax=plt.subplots(1,1,figsize=(8,6))
|
|
541
|
+
|
|
542
|
+
ax.set_ylabel('Stress, [MPa]')
|
|
543
|
+
ax.set_xlabel('Strain [*]')
|
|
544
|
+
ax.errorbar(mean_stress_strains['strain'],mean_stress_strains['stress'],stds_stress_strains['stress'],alpha=0.2)
|
|
545
|
+
ax.plot(mean_stress_strains['strain'],mean_stress_strains['stress'])
|
|
546
|
+
E,R2=compute_E(mean_stress_strains['strain'],mean_stress_strains['stress'],fit_domain=fit_domain)
|
|
547
|
+
fitline=E*mean_stress_strains['strain']
|
|
548
|
+
half_domain=int(mean_stress_strains['strain'].shape[0]/2)
|
|
549
|
+
line_domain=[0,fit_domain[1] if fit_domain[1]>half_domain else half_domain]
|
|
550
|
+
X=np.array(mean_stress_strains['strain'])[line_domain[0]:line_domain[1]]
|
|
551
|
+
Y=np.array(fitline)[line_domain[0]:line_domain[1]]
|
|
552
|
+
ax.plot(X,Y,'k--',alpha=0.7)
|
|
553
|
+
plt.savefig(outfile,bbox_inches='tight')
|
|
554
|
+
plt.close(fig)
|
|
555
|
+
logger.info(f'{outfile} and {save_data} created. E = {E/1000.0:.3f} GPa (R^2 {R2:.3f})')
|
|
556
|
+
|
|
557
|
+
def post_plots(args):
|
|
558
|
+
n_points=args.n_points
|
|
559
|
+
phases=[]
|
|
560
|
+
# print(args.cfg)
|
|
561
|
+
for c in args.cfg:
|
|
562
|
+
with open(c,'r') as f:
|
|
563
|
+
phases+=yaml.safe_load(f)
|
|
564
|
+
phasenames=[list(x.keys())[0] for x in phases]
|
|
565
|
+
# print(phasenames)
|
|
566
|
+
mdf=[]
|
|
567
|
+
for p in args.proj:
|
|
568
|
+
if 'anneal' in phasenames and 'equilibrate' in phasenames:
|
|
569
|
+
mdf.append(postsim_density_evolution(p))
|
|
570
|
+
if mdf:
|
|
571
|
+
multi_trace(mdf,xnames=['time(ps)']*len(mdf),ynames=['Density']*len(mdf),labels=args.proj,ylabel='Density [kg/m$^3$]',outfile='anneal-equil-density.png')
|
|
572
|
+
logger.info('-'.join(phasenames)+'-density.png created.')
|
|
573
|
+
m=[]
|
|
574
|
+
for d in mdf:
|
|
575
|
+
seg=d.iloc[int(0.9*d.shape[0]):]
|
|
576
|
+
logger.debug(f'averaging {seg.shape[0]} final density values out of {d.shape[0]}')
|
|
577
|
+
m.append(seg['Density'].mean())
|
|
578
|
+
m=np.array(m)
|
|
579
|
+
logger.info(f'mean density {m.mean():.0f} kg/m^3 ({m.mean()/1000.0:.3f} g/cc)')
|
|
580
|
+
|
|
581
|
+
ladder_phases=[idx for idx, value in enumerate(phasenames) if value == 'ladder']
|
|
582
|
+
# print(ladder_phases)
|
|
583
|
+
if len(ladder_phases)>0: # perform Tg calculations on each phase
|
|
584
|
+
do_tg_plots([phases[i] for i in ladder_phases],args.proj,n_points=n_points)
|
|
585
|
+
|
|
586
|
+
deform_phases=[idx for idx,value in enumerate(phasenames) if value == 'deform']
|
|
587
|
+
if len(deform_phases)>0:
|
|
588
|
+
do_E_plots([phases[i] for i in deform_phases],args.proj)
|