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,444 @@
1
+ """Handles the postsim subcommand.
2
+
3
+ Author: Cameron F. Abrams <cfa22@drexel.edu>
4
+ """
5
+ import json
6
+ import logging
7
+ import os
8
+ import shutil
9
+
10
+ import yaml
11
+
12
+ import numpy as np
13
+
14
+ from ..analysis.plot import scatter
15
+ from ..core import projectfilesystem as pfs
16
+ from ..core.configuration import Configuration
17
+ from ..core.topocoord import TopoCoord
18
+ from ..external import software as software
19
+ from ..external.gromacs import mdp_get, mdp_modify, gmx_energy_trace
20
+ from ..utils.logsetup import setup_logging
21
+
22
+ logger=logging.getLogger(__name__)
23
+
24
+ class PostSimMD:
25
+ """ Generic class for handling post-cure md simulations; this one just does simple NPT MD equilibration;
26
+ Classes that inherit from this class should define their own default_params and build_npt
27
+ """
28
+ default_params={
29
+ 'subdir': f'{pfs.Dirs.postsim}/equilibrate',
30
+ 'input_top': f'{pfs.Dirs.systems_final}/final.top',
31
+ 'input_gro': f'{pfs.Dirs.systems_final}/final.gro',
32
+ 'input_grx': f'{pfs.Dirs.systems_final}/final.grx',
33
+ 'gromacs' : {
34
+ 'gmx': 'gmx',
35
+ 'mdrun': 'gmx mdrun',
36
+ 'options': '-quiet -nobackup',
37
+ 'mdrun_single_molecule': 'gmx mdrun mdrun'
38
+ },
39
+ 'ps': 1000,
40
+ 'T': 300,
41
+ 'P':1,
42
+ 'output_deffnm': 'equilibrate',
43
+ 'traces': ['Temperature','Density','Volume'],
44
+ 'scatter': ('time(ps)',['Density'],'rho_v_ns.png')
45
+ }
46
+ def __init__(self,indict,strict=True):
47
+ self.params={}
48
+ for p,v in self.default_params.items():
49
+ self.params[p]=indict.get(p,v)
50
+ for p,v in indict.items():
51
+ if not p in self.default_params:
52
+ if strict:
53
+ logger.info(f'Ignoring directive \'{p}\' in yaml input file')
54
+ else:
55
+ self.params[p]=v
56
+
57
+ def do(self,mdp_pfx='npt',**gromacs_dict):
58
+ """Handles executing the postsim MD simulation.
59
+
60
+ Args:
61
+ mdp_pfx (str): filename prefix for output files, defaults to 'npt'
62
+ """
63
+ p=self.params
64
+ logger.info(f'do {p}')
65
+ # if a gromacs dict is passed in, assume this overrides the one read in from the file
66
+ if gromacs_dict:
67
+ software.set_gmx_preferences(gromacs_dict)
68
+ else:
69
+ software.set_gmx_preferences(p['gromacs'])
70
+ logger.info(f'going to {p["subdir"]}')
71
+ pfs.go_to(p['subdir'])
72
+ for input_file in ['input_top','input_gro','input_grx']:
73
+ srcnm=os.path.join(pfs.proj(),p[input_file])
74
+ shutil.copy(srcnm,'.')
75
+ local_top=os.path.basename(p['input_top'])
76
+ local_gro=os.path.basename(p['input_gro'])
77
+ local_grx=os.path.basename(p['input_grx'])
78
+ TC=TopoCoord(topfilename=local_top,grofilename=local_gro,grxfilename=local_grx)
79
+ logger.info(f'{TC.Coordinates.A.shape[0]} atoms {TC.Topology.total_mass(units="gromacs"):.2f} amu')
80
+ box=TC.Coordinates.box
81
+ pfs.checkout(pfs.Dirs.mdp_file('npt'))
82
+ os.rename('npt.mdp',f'{mdp_pfx}.mdp')
83
+ self.build_mdp(f'{mdp_pfx}.mdp',box=box)
84
+ msg=TC.grompp_and_mdrun(out=p['output_deffnm'],mdp=mdp_pfx,quiet=False,mylogger=logger.info,**gromacs_dict)
85
+ df=gmx_energy_trace(p['output_deffnm'],p['traces'])
86
+ use_scatter=p['scatter']
87
+ temp_x=None
88
+ temp_y=None
89
+ scat_file=use_scatter[2]
90
+ for sznm in ['Box-X','Box-Y','Box-Z']:
91
+ if sznm in p['traces']:
92
+ L0=box[0][0] if sznm=='Box-X' else box[1][1] if sznm=='Box-Y' else box[2][2]
93
+ temp_x=f'{sznm}-strain'
94
+ df[temp_x]=df[sznm]/L0-1.0
95
+ for sznm in ['Pres-XX','Pres-YY','Pres-ZZ']:
96
+ if sznm in p['traces']:
97
+ df[f'{sznm}-stress']=df[sznm]*(-1)
98
+ temp_y=[f'{sznm}-stress']
99
+ if temp_x and temp_y:
100
+ use_scatter=(temp_x,temp_y,scat_file)
101
+ df.to_csv(f'{p["output_deffnm"]}.csv',header=True,index=False)
102
+ scatter(df,*use_scatter)
103
+ logger.info(f'Final coordinates in {p["output_deffnm"]}.gro')
104
+ logger.info(f'Traces saved in {p["output_deffnm"]}.csv')
105
+
106
+ def build_mdp(self,mdpname,**kwargs):
107
+ """Builds the GROMACS mdp file required for an NPT equilibration.
108
+
109
+ Args:
110
+ mdpname (str): name of mdp file
111
+ """
112
+ params=self.params
113
+ timestep=float(mdp_get(mdpname,'dt'))
114
+ duration=params['ps']
115
+ nsteps=int(duration/timestep)
116
+ mod_dict={
117
+ 'ref_t':params['T'],
118
+ 'ref_p':params['P'],
119
+ 'gen-temp':params['T'],
120
+ 'gen-vel':'yes',
121
+ 'nsteps':nsteps,
122
+ 'tcoupl':'v-rescale','tau_t':0.5
123
+ }
124
+ mdp_modify(mdpname,mod_dict)
125
+
126
+ class PostSimAnneal(PostSimMD):
127
+ """ a class to handle temperature annealing MD simulation
128
+ """
129
+ default_params={
130
+ 'subdir': f'{pfs.Dirs.postsim}/anneal',
131
+ 'input_top': f'{pfs.Dirs.systems_final}/final.top',
132
+ 'input_gro': f'{pfs.Dirs.systems_final}/final.gro',
133
+ 'input_grx': f'{pfs.Dirs.systems_final}/final.grx',
134
+ 'gromacs' : {
135
+ 'gmx': 'gmx',
136
+ 'mdrun': 'gmx mdrun',
137
+ 'options': '-quiet -nobackup',
138
+ 'mdrun_single_molecule': 'gmx mdrun'
139
+ },
140
+ 'output_deffnm':'anneal',
141
+ 'traces': ['Temperature','Density','Volume'],
142
+ 'scatter': ('time(ps)',['Density'],'rho_v_ns.png'),
143
+ 'T0': 300,
144
+ 'T1': 600,
145
+ 'ncycles': 1,
146
+ 'T0_to_T1_ps': 1000,
147
+ 'T1_ps': 1000,
148
+ 'T1_to_T0_ps': 1000,
149
+ 'T0_ps': 1000,
150
+ 'P':1
151
+ }
152
+ def build_mdp(self,mdpname,**kwargs):
153
+ """Builds the GROMACS mdp file required for an annealing MD simulation.
154
+
155
+ Args:
156
+ mdpname (str): name of mdp file
157
+ """
158
+ params=self.params
159
+ timestep=float(mdp_get(mdpname,'dt'))
160
+ timeints=[0.0,params['T0_to_T1_ps'],params['T1_ps'],params['T1_to_T0_ps'],params['T0_ps']]
161
+ timepoints=[0.0]
162
+ temppoints=[params['T0'],params['T1'],params['T1'],params['T0'],params['T0']]
163
+ for i in range(1,len(timeints)):
164
+ timepoints.append(timepoints[-1]+timeints[i])
165
+ duration=timepoints[-1]
166
+ nsteps=int(duration/timestep)*params['ncycles']
167
+ mod_dict={
168
+ 'ref_t':params['T0'],
169
+ 'ref_p':params['P'],
170
+ 'gen-temp':params['T0'],
171
+ 'gen-vel':'yes',
172
+ 'annealing-npoints':len(timepoints),
173
+ 'annealing-temp':' '.join([str(x) for x in temppoints]),
174
+ 'annealing-time':' '.join([str(x) for x in timepoints]),
175
+ 'annealing':'periodic' if params['ncycles']>1 else 'single',
176
+ 'nsteps':nsteps,
177
+ 'tcoupl':'v-rescale','tau_t':0.5
178
+ }
179
+ mdp_modify(mdpname,mod_dict)
180
+
181
+ class PostSimLadder(PostSimMD):
182
+ """ a class to handle a temperature-ladder MD simulation
183
+ """
184
+ default_params={
185
+ 'subdir': f'{pfs.Dirs.postsim}/ladder',
186
+ 'input_top': f'{pfs.Dirs.systems_final}/final.top',
187
+ 'input_gro': f'{pfs.Dirs.systems_final}/final.gro',
188
+ 'input_grx': f'{pfs.Dirs.systems_final}/final.grx',
189
+ 'gromacs' : {
190
+ 'gmx': 'gmx',
191
+ 'mdrun': 'gmx mdrun',
192
+ 'options': '-quiet -nobackup',
193
+ 'mdrun_single_molecule': 'gmx mdrun mdrun'
194
+ },
195
+ 'output_deffnm':'ladder',
196
+ 'traces': ['Temperature','Density','Volume'],
197
+ 'scatter': ('time(ps)',['Density'],'rho_v_ns.png'),
198
+ 'Tlo':300.0,
199
+ 'Thi':600.0,
200
+ 'deltaT':5,
201
+ 'ps_per_run':1000,
202
+ 'ps_per_rise':1000,
203
+ 'warmup_ps':5000,
204
+ 'P':1
205
+ }
206
+
207
+ def build_mdp(self,mdpname,**kwargs):
208
+ """Builds the GROMACS mdp file required for a temperature-ladder MD simulation.
209
+
210
+ Args:
211
+ mdpname (str): name of mdp file
212
+ """
213
+ params=self.params
214
+ timestep=float(mdp_get(mdpname,'dt'))
215
+ Tdomain=params['Thi']-params['Tlo']
216
+ nSteps=int(Tdomain/np.abs(params['deltaT']))+1
217
+ T0=params['Tlo'] if params['deltaT']>0.0 else params['Thi']
218
+ T1=params['Thi'] if params['deltaT']>0.0 else params['Tlo']
219
+ Tladder=np.linspace(T0,T1,nSteps)
220
+ timepoints=[0.0,params['warmup_ps']]
221
+ temppoints=[T0,T0]
222
+ for i in range(nSteps):
223
+ cT=Tladder[i]
224
+ timepoints.append(timepoints[-1]+params['ps_per_rise'])
225
+ temppoints.append(cT)
226
+ timepoints.append(timepoints[-1]+params['ps_per_run'])
227
+ temppoints.append(cT)
228
+ duration=timepoints[-1]
229
+ nMDsteps=int(duration/timestep)
230
+ mod_dict={
231
+ 'ref_t':T0,
232
+ 'ref_p':params['P'],
233
+ 'gen-temp':T0,
234
+ 'gen-vel':'yes',
235
+ 'annealing-npoints':len(timepoints),
236
+ 'annealing-temp':' '.join([str(x) for x in temppoints]),
237
+ 'annealing-time':' '.join([str(x) for x in timepoints]),
238
+ 'annealing':'single',
239
+ 'nsteps':nMDsteps,
240
+ 'tcoupl':'v-rescale','tau_t':0.5
241
+ }
242
+ mdp_modify(mdpname,mod_dict)
243
+
244
+ class PostSimDeform(PostSimMD):
245
+ """ a class to handle a uniaxial deformation MD simulation
246
+ """
247
+ default_params={
248
+ 'subdir': f'{pfs.Dirs.postsim}/deform-x',
249
+ 'input_top': f'{pfs.Dirs.systems_final}/final.top',
250
+ 'input_gro': f'{pfs.Dirs.postsim}/equilibrate/equilibrate.gro',
251
+ 'input_grx': f'{pfs.Dirs.systems_final}/final.grx',
252
+ 'gromacs' : {
253
+ 'gmx': 'gmx',
254
+ 'mdrun': 'gmx mdrun',
255
+ 'options': '-quiet -nobackup',
256
+ 'mdrun_single_molecule': 'gmx mdrun mdrun'
257
+ },
258
+ 'output_deffnm':'deform-x',
259
+ 'traces': ['Box-X','Pres-XX'],
260
+ 'scatter': ('Box-X',['Pres-XX'],'tension_v_xlength.png'),
261
+ 'direction':'x',
262
+ 'T':300.0,
263
+ 'P':1.0,
264
+ 'ps':1000,
265
+ 'edot': 0.001 # strain rate in ps^-1
266
+ }
267
+
268
+ def build_mdp(self,mdpname,**kwargs):
269
+ """Builds the GROMACS mdp file required for a uniaxial deformation MD simulation.
270
+
271
+ Args:
272
+ mdpname (str): name of mdp file
273
+ """
274
+ params=self.params
275
+ timestep=float(mdp_get(mdpname,'dt'))
276
+ duration=params['ps']
277
+ nsteps=int(duration/timestep)
278
+ box=kwargs.get('box',np.array([[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]]))
279
+ edot=params.get('edot',0.0)
280
+ direction=params.get('direction','x')
281
+ mod_dict={
282
+ 'ref_t':params['T'],
283
+ 'ref_p':params['P'],
284
+ 'gen-temp':params['T'],
285
+ 'gen-vel':'yes',
286
+ 'tcoupl':'v-rescale',
287
+ 'nsteps': nsteps,
288
+ 'rlist': 1.2,
289
+ 'rcoulomb': 1.2,
290
+ 'rvdw': 1.2,
291
+ 'tau_t':0.5,
292
+ 'tau_p':1.0,
293
+ 'refcoord_scaling': 'com',
294
+ 'pcoupltype': 'anisotropic'
295
+ }
296
+ if direction=='x':
297
+ strain_vel=box[0][0]*edot
298
+ mod_dict['ref_p']='0.0 1.0 1.0 0 0 0'
299
+ mod_dict['compressibility']='0.0 4.5e-5 4.5e-5 0 0 0'
300
+ mod_dict['deform']=f'{strain_vel:.3e} 0 0 0 0 0'
301
+ params['output_deffnm'] = 'deform-x'
302
+ params['traces']=['Box-X','Pres-XX']
303
+ params['scatter']=('Box-X',['Pres-XX'],'tension_v_xlength.png')
304
+ elif direction=='y':
305
+ strain_vel=box[1][1]*edot
306
+ mod_dict['ref_p']='1.0 0.0 1.0 0 0 0'
307
+ mod_dict['compressibility']='4.5e-5 0.0 4.5e-5 0 0 0'
308
+ mod_dict['deform']=f'0 {strain_vel:.3e} 0 0 0 0'
309
+ params['output_deffnm'] = 'deform-y'
310
+ params['traces']=['Box-Y','Pres-YY']
311
+ params['scatter']=('Box-Y',['Pres-YY'],'tension_v_ylength.png')
312
+ elif direction=='z':
313
+ strain_vel=box[2][2]*edot
314
+ mod_dict['ref_p']='1.0 1.0 0.0 0 0 0'
315
+ mod_dict['compressibility']='4.5e-5 4.5e-5 0.0 0 0 0'
316
+ mod_dict['deform']=f'0 0 {strain_vel:.3e} 0 0 0'
317
+ params['output_deffnm'] = 'deform-z'
318
+ params['traces']=['Box-Z','Pres-ZZ']
319
+ params['scatter']=('Box-Z',['Pres-ZZ'],'tension_v_zlength.png')
320
+ else:
321
+ logger.error(f'Bad direction for uniaxial strain {direction}')
322
+
323
+ mdp_modify(mdpname,mod_dict)
324
+
325
+ class PostsimConfiguration:
326
+ """ handles reading and parsing a postcure simulation input config file.
327
+ Config file format
328
+
329
+ - { key1: {<paramdict>}}
330
+ - { key2: {<paramdict>}}
331
+
332
+ ...
333
+
334
+ The config file is a list of single-element dictionaries, whose single keyword
335
+ indicates the type of MD simulation to be run; simulations are run in the order
336
+ they appear in the config file.
337
+
338
+ Currently allowed simulation types:
339
+
340
+ - 'equilibrate': simple NPT equilibration;
341
+ - 'anneal': simple simulated annealing;
342
+ - 'ladder': temperature ladder;
343
+ - 'deform: constant strain-rate uniaxial deformation;
344
+
345
+ """
346
+ default_classes={'equilibrate':PostSimMD,'anneal':PostSimAnneal,'ladder':PostSimLadder,'deform':PostSimDeform}
347
+ def __init__(self):
348
+ self.cfgFile=''
349
+ self.baselist=[]
350
+ self.stagelist=[]
351
+
352
+ @classmethod
353
+ def read(cls,filename,parse=True,**kwargs):
354
+ """Generates a new PostsimConfiguration object by reading in the JSON or YAML file indicated by filename.
355
+
356
+ Args:
357
+ filename (str): name of file from which to read new PostsimConfiguration object
358
+ parse (bool): if True, parse the input configuration file, defaults to True
359
+
360
+ Raises:
361
+ Exception: if extension of filename is not '.json' or '.yaml' or '.yml'
362
+
363
+ Returns:
364
+ PostsimConfiguration: a new PostsimConfiguration object
365
+ """
366
+ basename,extension=os.path.splitext(filename)
367
+ if extension=='.json':
368
+ return cls._read_json(filename,parse,**kwargs)
369
+ elif extension=='.yaml' or extension=='.yml':
370
+ return cls._read_yaml(filename,parse,**kwargs)
371
+ else:
372
+ raise Exception(f'Unknown config file extension {extension}')
373
+
374
+ @classmethod
375
+ def _read_json(cls,filename,parse=True,**kwargs):
376
+ """Creates a new PostsimConfiguration object by reading from JSON input.
377
+
378
+ Args:
379
+ filename (str): name of JSON file
380
+ parse (bool): if True, parse the JSON data, defaults to True
381
+
382
+ Returns:
383
+ PostsimConfiguration: a new PostsimConfiguration object
384
+ """
385
+ inst=cls()
386
+ inst.cfgFile=filename
387
+ with open(filename,'r') as f:
388
+ inst.baselist=json.load(f)
389
+ assert type(inst.baselist)==list,f'Poorly formatted {filename}'
390
+ if parse: inst.parse(**kwargs)
391
+ return inst
392
+
393
+ @classmethod
394
+ def _read_yaml(cls,filename,parse=True,**kwargs):
395
+ """Creates a new PostsimConfiguration object by reading from YAML input.
396
+
397
+ Args:
398
+ filename (str): name of YAML file
399
+ parse (bool): if True, parse the YAML data, defaults to True
400
+
401
+ Returns:
402
+ PostsimConfiguration: a new PostsimConfiguration object
403
+ """
404
+ inst=cls()
405
+ inst.cfgFile=filename
406
+ with open(filename,'r') as f:
407
+ inst.baselist=yaml.safe_load(f)
408
+ assert type(inst.baselist)==list,f'Poorly formatted {filename}'
409
+ if parse: inst.parse(**kwargs)
410
+ return inst
411
+
412
+ def parse(self,**kwargs):
413
+ """Parses a PostsimConfiguration file to build the list of stages to run."""
414
+ for p in self.baselist:
415
+ assert len(p)==1,f'Poorly formatted {self.cfgFile}; each stanza may have only one keyword'
416
+ simtype=list(p.keys())[0]
417
+ assert simtype in self.default_classes,f'Simulation type "{simtype}" in {self.cfgFile} not understood.'
418
+ logger.info(f'passing in {p[simtype]}')
419
+ self.stagelist.append(self.default_classes[simtype](p[simtype]))
420
+
421
+ def postsim(args):
422
+ """Handles the postsim subcommand for managing post-cure production MD simulations.
423
+
424
+ Args:
425
+ args (argparse.Namespace): command-line arguments
426
+ """
427
+ setup_logging(args.loglevel, no_banner=args.no_banner)
428
+ ess='y' if len(args.proj)==0 else 'ies'
429
+ ogromacs={}
430
+ if args.ocfg:
431
+ ocfg=Configuration.read(args.ocfg)
432
+ ogromacs=ocfg.gromacs
433
+ cfg=PostsimConfiguration.read(args.cfg)
434
+ logger.debug(f'{cfg.baselist}')
435
+ logger.info(f'Project director{ess}: {args.proj}')
436
+ software.sw_setup()
437
+ logger.debug(f'ogromacs {ogromacs}')
438
+ for d in args.proj:
439
+ pfs.pfs_setup(root=os.getcwd(),topdirs=pfs.Dirs.postsim_topdirs,verbose=True,projdir=d,reProject=False,userlibrary=args.lib)
440
+ pfs.go_to(pfs.Dirs.postsim)
441
+ for stage in cfg.stagelist:
442
+ stage.do(mdp_pfx='local',**ogromacs)
443
+ pfs.go_root()
444
+