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.
Files changed (110) hide show
  1. htpolynet/__init__.py +2 -0
  2. htpolynet/analysis/__init__.py +0 -0
  3. htpolynet/analysis/analyze.py +256 -0
  4. htpolynet/analysis/plot.py +588 -0
  5. htpolynet/analysis/postsim.py +444 -0
  6. htpolynet/analysis/utils.py +399 -0
  7. htpolynet/cli.py +488 -0
  8. htpolynet/core/__init__.py +0 -0
  9. htpolynet/core/bondtemplate.py +112 -0
  10. htpolynet/core/configuration.py +83 -0
  11. htpolynet/core/coordinates.py +644 -0
  12. htpolynet/core/molecule.py +998 -0
  13. htpolynet/core/projectfilesystem.py +616 -0
  14. htpolynet/core/runtime.py +774 -0
  15. htpolynet/core/topocoord.py +2044 -0
  16. htpolynet/core/topology.py +1138 -0
  17. htpolynet/cure/__init__.py +0 -0
  18. htpolynet/cure/chain.py +135 -0
  19. htpolynet/cure/curecontroller.py +746 -0
  20. htpolynet/cure/expandreactions.py +294 -0
  21. htpolynet/cure/reaction.py +280 -0
  22. htpolynet/external/__init__.py +0 -0
  23. htpolynet/external/ambertools.py +97 -0
  24. htpolynet/external/command.py +73 -0
  25. htpolynet/external/gromacs.py +308 -0
  26. htpolynet/external/slurm.py +111 -0
  27. htpolynet/external/software.py +183 -0
  28. htpolynet/geometry/__init__.py +0 -0
  29. htpolynet/geometry/bondlist.py +205 -0
  30. htpolynet/geometry/lattice.py +32 -0
  31. htpolynet/geometry/linkcell.py +210 -0
  32. htpolynet/geometry/matrix4.py +110 -0
  33. htpolynet/geometry/ring.py +274 -0
  34. htpolynet/io/__init__.py +6 -0
  35. htpolynet/io/gro.py +195 -0
  36. htpolynet/io/mol2.py +210 -0
  37. htpolynet/io/pdb.py +157 -0
  38. htpolynet/resources/README.md +20 -0
  39. htpolynet/resources/__init__.py +0 -0
  40. htpolynet/resources/cfg/DGE-PAC-hi.yaml +115 -0
  41. htpolynet/resources/cfg/DGE-PAC-lo.yaml +115 -0
  42. htpolynet/resources/cfg/FDE-DFDA-hi.yaml +116 -0
  43. htpolynet/resources/cfg/FDE-DFDA-lo.yaml +116 -0
  44. htpolynet/resources/cfg/GMASTY-hi.yaml +158 -0
  45. htpolynet/resources/cfg/GMASTY-lo.yaml +158 -0
  46. htpolynet/resources/cfg/README.md +1 -0
  47. htpolynet/resources/cfg/STY.yaml +23 -0
  48. htpolynet/resources/cfg/pMSTY-hi.yaml +101 -0
  49. htpolynet/resources/cfg/pMSTY-lo.yaml +101 -0
  50. htpolynet/resources/cfg/pSTY-hi.yaml +101 -0
  51. htpolynet/resources/cfg/pSTY-lo.yaml +101 -0
  52. htpolynet/resources/example_depot/0-liquid-styrene.sh +134 -0
  53. htpolynet/resources/example_depot/0-liquid-styrene.tgz +0 -0
  54. htpolynet/resources/example_depot/1-polystyrene.sh +202 -0
  55. htpolynet/resources/example_depot/1-polystyrene.tgz +0 -0
  56. htpolynet/resources/example_depot/2-bisgma-styrene-thermoset.sh +353 -0
  57. htpolynet/resources/example_depot/2-bisgma-styrene-thermoset.tgz +0 -0
  58. htpolynet/resources/example_depot/3-pacm-dgeba-epoxy-thermoset.sh +242 -0
  59. htpolynet/resources/example_depot/3-pacm-dgeba-epoxy-thermoset.tgz +0 -0
  60. htpolynet/resources/example_depot/4-dfda-fde-epoxy-thermoset.sh +216 -0
  61. htpolynet/resources/example_depot/4-dfda-fde-epoxy-thermoset.tgz +0 -0
  62. htpolynet/resources/mdp/README.md +1 -0
  63. htpolynet/resources/mdp/drag-min.mdp +13 -0
  64. htpolynet/resources/mdp/drag-npt.mdp +36 -0
  65. htpolynet/resources/mdp/drag-nvt.mdp +31 -0
  66. htpolynet/resources/mdp/min.mdp +14 -0
  67. htpolynet/resources/mdp/npt.mdp +37 -0
  68. htpolynet/resources/mdp/nvt.mdp +29 -0
  69. htpolynet/resources/mdp/relax-min.mdp +13 -0
  70. htpolynet/resources/mdp/relax-npt.mdp +37 -0
  71. htpolynet/resources/mdp/relax-nvt.mdp +31 -0
  72. htpolynet/resources/mdp/single-molecule-min.mdp +12 -0
  73. htpolynet/resources/mdp/single-molecule-nvt.mdp +25 -0
  74. htpolynet/resources/molecules/inputs/DFA.pdb +62 -0
  75. htpolynet/resources/molecules/inputs/DGE.mol2 +115 -0
  76. htpolynet/resources/molecules/inputs/EMB.mol2 +50 -0
  77. htpolynet/resources/molecules/inputs/FDE.pdb +72 -0
  78. htpolynet/resources/molecules/inputs/GMA.mol2 +163 -0
  79. htpolynet/resources/molecules/inputs/PAC.mol2 +91 -0
  80. htpolynet/resources/molecules/inputs/STY.mol2 +44 -0
  81. htpolynet/resources/molecules/make-monomers.sh +64 -0
  82. htpolynet/resources/molecules/pics/DFA.png +0 -0
  83. htpolynet/resources/molecules/pics/DGE.png +0 -0
  84. htpolynet/resources/molecules/pics/EMB.png +0 -0
  85. htpolynet/resources/molecules/pics/FDE.png +0 -0
  86. htpolynet/resources/molecules/pics/GMA.png +0 -0
  87. htpolynet/resources/molecules/pics/PAC.png +0 -0
  88. htpolynet/resources/molecules/pics/STY.png +0 -0
  89. htpolynet/resources/molecules/sample-inputs/DFA.pdb +62 -0
  90. htpolynet/resources/molecules/sample-inputs/DGE.mol2 +115 -0
  91. htpolynet/resources/molecules/sample-inputs/EMB.mol2 +50 -0
  92. htpolynet/resources/molecules/sample-inputs/FDE.pdb +72 -0
  93. htpolynet/resources/molecules/sample-inputs/GMA.mol2 +163 -0
  94. htpolynet/resources/molecules/sample-inputs/PAC.mol2 +91 -0
  95. htpolynet/resources/molecules/sample-inputs/STY.mol2 +44 -0
  96. htpolynet/resources/tcl/readbonds.tcl +32 -0
  97. htpolynet/resources/tcl/readgrx.tcl +46 -0
  98. htpolynet/resources/tcl/render.tcl +74 -0
  99. htpolynet/utils/__init__.py +0 -0
  100. htpolynet/utils/banner.py +34 -0
  101. htpolynet/utils/checkpoint.py +85 -0
  102. htpolynet/utils/dataframetools.py +92 -0
  103. htpolynet/utils/inputcheck.py +49 -0
  104. htpolynet/utils/logsetup.py +44 -0
  105. htpolynet/utils/stringthings.py +39 -0
  106. htpolynet-2.0.0.dist-info/METADATA +78 -0
  107. htpolynet-2.0.0.dist-info/RECORD +110 -0
  108. htpolynet-2.0.0.dist-info/WHEEL +4 -0
  109. htpolynet-2.0.0.dist-info/entry_points.txt +2 -0
  110. htpolynet-2.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,399 @@
1
+ """Various utility methods for plotting and postprocessing.
2
+
3
+ Author: Cameron F. Abrams <cfa22@drexel.edu>
4
+ """
5
+ import logging
6
+ import os
7
+
8
+ import networkx as nx
9
+ import numpy as np
10
+ import pandas as pd
11
+
12
+ from scipy.constants import physical_constants
13
+ from scipy.optimize import curve_fit
14
+
15
+ from ..core.coordinates import Coordinates
16
+ from ..core.topocoord import TopoCoord
17
+ from ..external.gromacs import gmx_energy_trace
18
+
19
+ logger=logging.getLogger(__name__)
20
+
21
+ def density_from_gro(gro,mollib='./lib/molecules/parameterized',units='SI'):
22
+ """Computes density from a Gromacs gro file.
23
+
24
+ Args:
25
+ gro (str): name of gro file
26
+ mollib (str): location of parameterized molecular templates, defaults to './lib/molecules/parameterized'
27
+ units (str): string indicating unit system, defaults to 'SI'
28
+
29
+ Returns:
30
+ float: density
31
+ """
32
+ C=Coordinates.read_gro(gro)
33
+ resnames=list(set(C.A['resName']))
34
+ templates={x:TopoCoord.from_top_gro(os.path.join(mollib,f'{x}.top'),os.path.join(mollib,f'{x}.gro')) for x in resnames}
35
+ mass=0.0
36
+ counts={}
37
+ for nm,T in templates.items():
38
+ if not nm in counts:
39
+ counts[nm]={}
40
+ for an in T.Topology.D['atoms']['atom']:
41
+ counts[nm][an]=0
42
+ these=C.A[C.A['resName']==nm]['atomName']
43
+ for n in these:
44
+ counts[nm][n]+=1
45
+ for nm,adict in counts.items():
46
+ T=templates[nm].Topology.D['atoms']
47
+ for aname,c in adict.items():
48
+ mass+=c*T[T['atom']==aname]['mass'].values[0]
49
+ volume=np.prod(C.box.diagonal())
50
+ fac=1.0
51
+ if units=='SI':
52
+ mfac=physical_constants['atomic mass constant'][0]
53
+ lfac=1.e-9
54
+ vfac=lfac**3
55
+ fac=mfac/vfac
56
+ return fac*mass/volume
57
+
58
+ """ These globals are used in traversing a project directory to extract data from edr files """
59
+ _system_dirs=['densification','precure',r'iter-{iter:d}','capping','postcure']
60
+ _postsim_dirs=['anneal','equilibrate']
61
+ _measurement_dirs=['Tg','E']
62
+ _md_ensembles={'nvt':['Temperature','Potential'],'npt':['Temperature','Potential','Density'],'default':['Temperature','Potential']}
63
+ _indir_pfx={}
64
+ _indir_pfx['densification']=[r'densified-{ens:s}',r'densified-repeat-{repeat:d}-{ens:s}']
65
+ _indir_pfx['precure']=[r'preequilibration-{ens:s}',r'annealed',r'postequilibration-{ens:s}']
66
+ _indir_pfx['iter-n']=[r'1-cure_drag-stage-{stage:d}-{ens:s}',r'3-cure_relax-stage-{stage:d}-{ens:s}',r'4-cure_equilibrate-{ens:s}']
67
+ _indir_pfx['capping']=[r'7-cap_relax-stage-{stage:d}-{ens:s}',r'8-cap_equilibrate-{ens:s}']
68
+ _indir_pfx['postcure']=[r'preequilibration-{ens:s}',r'annealed',r'postequilibration-{ens:s}']
69
+ def _concat_from_edr(df,edr,names,add=[],add_if_missing=[('Density',0.0)]):
70
+ """Concatenates rows onto dataframe df by reading data from an edr file.
71
+
72
+ Args:
73
+ df (pandas.DataFrame): a dataframe with data read in from edr files
74
+ edr (str): name of a new edr file to read from
75
+ names (list of str): names of energy-like quantities to be read in; each must have a column in df already
76
+ add (list): names and values to add to dataset, defaults to []
77
+ add_if_missing (list): names and values to add if not found in edr file, defaults to [('Density',0.0)]
78
+
79
+ Returns:
80
+ tuple: the dataframe and the scalar value of last time before this data increment is read in
81
+ """
82
+ xshift=0.0
83
+ if not df.empty: xshift=df.iloc[-1]['time(ps)']
84
+ if len(names)==0: return df,xshift
85
+ # print(f'{add_if_missing}')
86
+ this_df=gmx_energy_trace(edr,names,xshift=xshift)
87
+ for m in add:
88
+ nm,val=m
89
+ this_df[nm]=np.ones(this_df.shape[0],dtype=type(val))*val
90
+ for m in add_if_missing:
91
+ nm,val=m
92
+ if not nm in this_df:
93
+ this_df[nm]=np.ones(this_df.shape[0],dtype=type(val))*val
94
+ df=pd.concat((df,this_df),ignore_index=True)
95
+ return df,xshift
96
+
97
+ def postsim_density_evolution(proj_dir,append_dirname=False):
98
+ """Returns a single dataframe that is a concatenation of the csv files in the 'postsim' subdirectories.
99
+
100
+ Args:
101
+ proj_dir (str): name of complete project directory
102
+
103
+ Returns:
104
+ pandas.DataFrame: the dataframe
105
+ """
106
+ if not os.path.exists(proj_dir): return
107
+ sysd=os.path.join(proj_dir,'postsim')
108
+ fn=[f'{sysd}/anneal/anneal.csv',f'{sysd}/equilibrate/equilibrate.csv']
109
+ df=pd.DataFrame()
110
+ lt=0.0
111
+ for f in fn:
112
+ t=pd.read_csv(f,header=0,index_col=None)
113
+ logger.debug(f'shifting by {lt}')
114
+ t['time(ps)']+=lt
115
+ lt=t['time(ps)'].to_list()[-1]
116
+ df=pd.concat((df,t))
117
+ if append_dirname:
118
+ df.rename(columns={x:f'{x}-{proj_dir}' for x in df.columns})
119
+ return df
120
+
121
+ def density_evolution(proj_dir):
122
+ """Returns a single dataframe containing density, temperature, number of bonds vs time by reading all edrs in the correct order from a complete project directory.
123
+
124
+ Args:
125
+ proj_dir (str): name of complete project directory
126
+
127
+ Returns:
128
+ pandas.DataFrame: the dataframe
129
+ """
130
+ if not os.path.exists(proj_dir): return
131
+ sysd=os.path.join(proj_dir,'systems')
132
+ df=pd.DataFrame()
133
+ xshift=0.0
134
+ transition_times=[xshift]
135
+ interval_labels=[]
136
+ markers=[]
137
+ nbonds=0
138
+ for subd in _system_dirs:
139
+ # print(f'subd {subd}')
140
+ if subd==r'iter-{iter:d}' or subd=='capping' or subd=='densification':
141
+ # loooking for iterations
142
+ # print(iter_mark0)
143
+ iter=1
144
+ iter_subd_rel=subd if subd in ['capping','densification'] else subd.format(iter=iter)
145
+ iter_subd=os.path.join(sysd,iter_subd_rel)
146
+ while os.path.exists(iter_subd):
147
+ logger.info(f'Reading edrs in {iter_subd_rel}...')
148
+ if r'iter' in subd:
149
+ markers.append(df.iloc[-1]['time(ps)'])
150
+ dirkey='iter-n' if subd==r'iter-{iter:d}' else subd
151
+ if subd==r'iter-{iter:d}':
152
+ if os.path.exists(os.path.join(iter_subd,'2-cure_update-bonds.csv')):
153
+ bdf=pd.read_csv(os.path.join(iter_subd,'2-cure_update-bonds.csv'),sep=r'\s+',header=0,index_col=None)
154
+ nbonds+=bdf.shape[0]
155
+ else:
156
+ if os.path.exists(os.path.join(iter_subd,'6-cap_update-bonds.csv')):
157
+ bdf=pd.read_csv(os.path.join(iter_subd,'6-cap_update-bonds.csv'),sep=r'\s+',header=0,index_col=None)
158
+ nbonds+=bdf.shape[0]
159
+ # print(f'iter_subd {iter_subd} dirkey {dirkey}')
160
+ for pfx in _indir_pfx[dirkey]:
161
+ # print(f'pfx {pfx}')
162
+ if r'stage' in pfx or r'repeat' in pfx:
163
+ stg=1
164
+ stg_present=any([os.path.exists(os.path.join(iter_subd,pfx.format(stage=stg,repeat=stg,ens=x))+r'.edr') for x in _md_ensembles])
165
+ # print(f'{iter_subd} {stg} {pfx} stg_present {stg_present}')
166
+ while stg_present:
167
+ for ens,qtys in _md_ensembles.items():
168
+ edr_pfx=os.path.join(iter_subd,pfx.format(stage=stg,repeat=stg,ens=ens))
169
+ gro=edr_pfx+r'.gro'
170
+ # print(edr_pfx,os.path.exists(edr_pfx+r'.edr'))
171
+ if os.path.exists(edr_pfx+r'.edr'):
172
+ density=0.0
173
+ if ens!='npt':
174
+ if os.path.exists(gro):
175
+ density=density_from_gro(gro,os.path.join(proj_dir,'molecules/parameterized'))
176
+ # print(f'density from gro {density}')
177
+ df,xshift=_concat_from_edr(df,edr_pfx,qtys,add=[('nbonds',nbonds)],add_if_missing=[('Density',density)])
178
+ transition_times.append(xshift)
179
+ interval_labels.append([edr_pfx])
180
+ stg+=1
181
+ stg_present=any([os.path.exists(os.path.join(iter_subd,pfx.format(stage=stg,repeat=stg,ens=x))+r'.edr') for x in _md_ensembles])
182
+ # print(f'->{iter_subd} {stg} {pfx} stg_present {stg_present}')
183
+ elif r'equil' in pfx or r'densif' in pfx:
184
+ eq_present=any([os.path.exists(os.path.join(iter_subd,pfx.format(ens=x))+r'.edr') for x in _md_ensembles])
185
+ # print(f'{iter_subd} eq_present? {eq_present}')
186
+ if eq_present:
187
+ for ens,qtys in _md_ensembles.items():
188
+ edr_pfx=os.path.join(iter_subd,pfx.format(ens=ens))
189
+ gro=edr_pfx+r'.gro'
190
+ # print(edr_pfx,os.path.exists(edr_pfx+r'.edr'),ens,qtys)
191
+ if os.path.exists(edr_pfx+r'.edr'):
192
+ density=0.0
193
+ if ens!='npt':
194
+ if os.path.exists(gro):
195
+ density=density_from_gro(gro,os.path.join(proj_dir,'molecules/parameterized'))
196
+ df,xshift=_concat_from_edr(df,edr_pfx,qtys,add=[('nbonds',nbonds)],add_if_missing=[('Density',density)])
197
+ transition_times.append(xshift)
198
+ interval_labels.append([edr_pfx])
199
+ iter+=1
200
+ iter_subd_rel=subd.format(iter=iter)
201
+ iter_subd=os.path.join(sysd,iter_subd_rel) if r'iter' in subd else 'I_BET_THIS_FILE_DNE'
202
+ # if r'iter' in subd: markers.append(df.iloc[-1]['time (ps)'])
203
+ else:
204
+ this_subd=os.path.join(sysd,subd)
205
+ logger.info(f'Reading edrs in {subd}...')
206
+
207
+ for pfx in _indir_pfx[subd]:
208
+ # print(f'this_subd {this_subd} pfx {pfx}')
209
+ if 'ens' in pfx:
210
+ for ens,qtys in _md_ensembles.items():
211
+ edr_pfx=os.path.join(this_subd,pfx.format(ens=ens))
212
+ gro=edr_pfx+r'.gro'
213
+ # print(edr_pfx,os.path.exists(edr_pfx+r'.edr'))
214
+ # print(gro,os.path.exists(gro))
215
+ if os.path.exists(edr_pfx+r'.edr'):
216
+ density=0.0
217
+ if ens=='nvt' and os.path.exists(gro):
218
+ density=density_from_gro(gro,os.path.join(proj_dir,'molecules/parameterized'))
219
+ df,xshift=_concat_from_edr(df,edr_pfx,qtys,add=[('nbonds',nbonds)],add_if_missing=[('Density',density)])
220
+ transition_times.append(xshift)
221
+ interval_labels.append([edr_pfx])
222
+ else:
223
+ edr_pfx=os.path.join(this_subd,pfx.format(ens=ens))
224
+ gro=edr_pfx+r'.gro'
225
+ # print(edr_pfx,os.path.exists(edr_pfx+r'.edr'))
226
+ if os.path.exists(edr_pfx+r'.edr'):
227
+ density=0.0
228
+ if os.path.exists(gro):
229
+ density=density_from_gro(gro,os.path.join(proj_dir,'molecules/parameterized'))
230
+ df,xshift=_concat_from_edr(df,edr_pfx,_md_ensembles['default'],add=[('nbonds',nbonds)],add_if_missing=[('Density',density)])
231
+ transition_times.append(xshift)
232
+ interval_labels.append([edr_pfx])
233
+ return df,transition_times,markers,interval_labels
234
+
235
+ def graph_from_bondsfile(bondsfile):
236
+ """Generates a networkx Graph in which each node is a molecule (from 'mi' and 'mj' records in the bondsfile) and edges indicate two molecules are joined by a covalent bond.
237
+
238
+ Args:
239
+ bondsfile (str): name of bondsfile
240
+
241
+ Returns:
242
+ networkx.Graph: the graph
243
+ """
244
+ G=nx.Graph()
245
+ df=pd.read_csv(bondsfile,header=0,index_col=None,sep=r'\s+')
246
+ for i,r in df.iterrows():
247
+ G.add_edge(r['mi'],r['mj'])
248
+ return G
249
+
250
+ def mwbxl(G:nx.Graph,crosslinker='GMA',monomer='STY'):
251
+ """Computes the histogram of monomer counts 'n' between crosslinking sites using a molecular connectivity graph; used mainly for vinyl-based polymerizations.
252
+
253
+ Args:
254
+ G (nx.Graph): molecular connectivity graph
255
+ crosslinker (str): name of crosslinker molecule, defaults to 'GMA'
256
+ monomer (str): name of monomer, defaults to 'STY'
257
+
258
+ Returns:
259
+ pd.DataFrame: a dataframe of 'n' and 'count'
260
+ """
261
+ xG=G.copy()
262
+ # traverse edges and label hetero/homo participants
263
+ for n in xG.nodes():
264
+ xG.nodes[n]['neighbor_count']=0
265
+ for u,v in xG.edges():
266
+ n,m=xG.nodes[u],xG.nodes[v]
267
+ n['neighbor_count']+=1
268
+ m['neighbor_count']+=1
269
+ if n['molecule_name']!=m['molecule_name']:
270
+ n['monomer_type']='hetero'
271
+ m['monomer_type']='hetero'
272
+ else:
273
+ n['monomer_type']='homo'
274
+ m['monomer_type']='homo'
275
+ wipe_us=[]
276
+ for n in xG.nodes():
277
+ if xG.nodes[n]['molecule_name']==crosslinker:
278
+ wipe_us.append(n)
279
+ xG.remove_nodes_from(wipe_us)
280
+ chaintype=[]
281
+ ch=nx.connected_components(xG)
282
+ mwhist={}
283
+ nb=0
284
+ for c in ch:
285
+ nhetero=0
286
+ for n in c:
287
+ node=xG.nodes[n]
288
+ if node.get('monomer_type','none')=='hetero':
289
+ nhetero+=1
290
+ if nhetero==0:
291
+ chaintype.append('isolated')
292
+ elif nhetero==1:
293
+ if len(c)==1:
294
+ node=list(xG.nodes.values())[0]
295
+ if node['neighbor_count']==1:
296
+ chaintype.append('dangling')
297
+ elif node['neighbor_count']==2:
298
+ chaintype.append('bridging')
299
+ if len(c) not in mwhist:
300
+ mwhist[len(c)]=0
301
+ mwhist[len(c)]+=1
302
+ else:
303
+ chaintype.append('dangling')
304
+ elif nhetero==2:
305
+ chaintype.append('bridging')
306
+ if len(c) not in mwhist:
307
+ mwhist[len(c)]=0
308
+ mwhist[len(c)]+=1
309
+ nb+=1
310
+ maxlen=max(list(mwhist.keys()))
311
+ mw=[]
312
+ counts=[]
313
+ for n in range(maxlen):
314
+ cnt=mwhist.get(n,0)
315
+ mw.append(n)
316
+ counts.append(cnt)
317
+ df=pd.DataFrame({'n':mw,'counts':counts})
318
+ return df
319
+
320
+ def clusters(G:nx.Graph):
321
+ """Performs a clustering analysis and returns a histogram of cluster sizes (in numbers of molecules) as a pandas DataFrame.
322
+
323
+ Args:
324
+ G (nx.Graph): molecular connectivity graph
325
+
326
+ Returns:
327
+ pd.DataFrame: cluster size histogram
328
+ """
329
+ counts={}
330
+ for c in sorted(nx.connected_components(G),key=len,reverse=True):
331
+ size=len(c)
332
+ if not size in counts:
333
+ counts[size]=0
334
+ counts[size]+=1
335
+ sizes=[]
336
+ numbers=[]
337
+ for s,c in counts.items():
338
+ sizes.append(s)
339
+ numbers.append(c)
340
+ df=pd.DataFrame({'sizes':sizes,'counts':numbers})
341
+ df.sort_values(by='sizes',ascending=False,inplace=True)
342
+ return df
343
+
344
+ def compute_tg(T,v,n_points=[10,20]):
345
+ """Performs a Tg determination from (volume or density) vs temperature data by fitting lines to the low-T region and another to the high-T region, taking Tg as the temperature at which they intersect.
346
+
347
+ Args:
348
+ T (numpy.array): temperature values
349
+ v (numpy.array): volume or density data
350
+ n_points (list): number of points on the low and high side to fit lines to, defaults to [10,20]
351
+ """
352
+ def func(x,a,b):
353
+ return x*a+b
354
+ hot_par=[]
355
+ cold_par=[]
356
+ x=np.array(T)[:n_points[0]]
357
+ y=np.array(v)[:n_points[0]]
358
+ # logger.info(f'Tg: {len(x)} data points on cold side; begins at {x[0]}')
359
+ cold_par,pcov=curve_fit(func,x,y)
360
+ sse=(y-func(x,*cold_par))**2
361
+ sst=(y-x.mean())**2
362
+ r2=1-sse.sum()/sst.sum()
363
+ # logger.info(f'cold fit: {cold_par} {r2}')
364
+
365
+ x=np.array(T)[-n_points[1]:]
366
+ y=np.array(v)[-n_points[1]:]
367
+ # logger.info(f'Tg: {len(x)} data points on hot side; ends at {x[-1]}')
368
+ hot_par,pcov=curve_fit(func,x,y)
369
+ sse=(y-func(x,*hot_par))**2
370
+ sst=(y-x.mean())**2
371
+ r2=1-sse.sum()/sst.sum()
372
+ # logger.info(f'hot fit: {hot_par} {r2}')
373
+ Tg=-1
374
+ if len(hot_par)>0 and len(cold_par)>0:
375
+ Tg=-(hot_par[1]-cold_par[1])/(hot_par[0]-cold_par[0])
376
+ return Tg,cold_par,hot_par
377
+
378
+ def compute_E(strain,stress,fit_domain=[10,100]):
379
+ """Computes the Young's modulus by performing a linear fit to an elastic regime in stress-vs-strain data.
380
+
381
+ Args:
382
+ strain (numpy.array): strain values
383
+ stress (numpy.array): stress values
384
+ fit_domain (list): domain over which fit is made, defaults to [10,100]
385
+
386
+ Returns:
387
+ tuple(float,float): E and R2 from fit
388
+ """
389
+ x=np.array(strain[fit_domain[0]:fit_domain[1]])
390
+ y=np.array(stress[fit_domain[0]:fit_domain[1]])
391
+ # logger.info(f'x: {x[0]} -> {x[-1]}')
392
+ # logger.info(f'y: {y[0]} -> {y[-1]}')
393
+ def func(x,a):
394
+ return a*x
395
+ popt,pcov=curve_fit(func,x,y,p0=(1000.0))
396
+ sse=(y-func(x,*popt))**2
397
+ sst=(y-y.mean())**2
398
+ r2=1-sse.sum()/sst.sum()
399
+ return popt[0],r2