mimicpy 0.2.0__py3-none-any.whl → 0.3.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 (53) hide show
  1. mimicpy/__init__.py +1 -1
  2. mimicpy/__main__.py +726 -2
  3. mimicpy/_authors.py +2 -2
  4. mimicpy/_version.py +2 -2
  5. mimicpy/coords/__init__.py +1 -1
  6. mimicpy/coords/base.py +1 -1
  7. mimicpy/coords/cpmdgeo.py +1 -1
  8. mimicpy/coords/gro.py +1 -1
  9. mimicpy/coords/pdb.py +1 -1
  10. mimicpy/core/__init__.py +1 -1
  11. mimicpy/core/prepare.py +3 -3
  12. mimicpy/core/selector.py +1 -1
  13. mimicpy/force_matching/__init__.py +34 -0
  14. mimicpy/force_matching/bonded_forces.py +628 -0
  15. mimicpy/force_matching/compare_top.py +809 -0
  16. mimicpy/force_matching/dresp.py +435 -0
  17. mimicpy/force_matching/nonbonded_forces.py +32 -0
  18. mimicpy/force_matching/opt_ff.py +2114 -0
  19. mimicpy/force_matching/qm_region.py +1960 -0
  20. mimicpy/plugins/__main_installer__.py +76 -0
  21. mimicpy/{__main_vmd__.py → plugins/__main_vmd__.py} +2 -2
  22. mimicpy/plugins/pymol.py +56 -0
  23. mimicpy/plugins/vmd.tcl +78 -0
  24. mimicpy/scripts/__init__.py +1 -1
  25. mimicpy/scripts/cpmd.py +1 -1
  26. mimicpy/scripts/fm_input.py +265 -0
  27. mimicpy/scripts/fmdata.py +120 -0
  28. mimicpy/scripts/mdp.py +1 -1
  29. mimicpy/scripts/ndx.py +1 -1
  30. mimicpy/scripts/script.py +1 -1
  31. mimicpy/topology/__init__.py +1 -1
  32. mimicpy/topology/itp.py +603 -35
  33. mimicpy/topology/mpt.py +1 -1
  34. mimicpy/topology/top.py +254 -15
  35. mimicpy/topology/topol_dict.py +233 -4
  36. mimicpy/utils/__init__.py +1 -1
  37. mimicpy/utils/atomic_numbers.py +1 -1
  38. mimicpy/utils/constants.py +17 -3
  39. mimicpy/utils/elements.py +1 -1
  40. mimicpy/utils/errors.py +1 -1
  41. mimicpy/utils/file_handler.py +1 -1
  42. mimicpy/utils/strings.py +1 -1
  43. mimicpy-0.3.0.dist-info/METADATA +156 -0
  44. mimicpy-0.3.0.dist-info/RECORD +50 -0
  45. {mimicpy-0.2.0.dist-info → mimicpy-0.3.0.dist-info}/WHEEL +1 -1
  46. mimicpy-0.3.0.dist-info/entry_points.txt +4 -0
  47. mimicpy-0.2.0.dist-info/METADATA +0 -86
  48. mimicpy-0.2.0.dist-info/RECORD +0 -38
  49. mimicpy-0.2.0.dist-info/entry_points.txt +0 -3
  50. {mimicpy-0.2.0.dist-info → mimicpy-0.3.0.dist-info/licenses}/COPYING +0 -0
  51. {mimicpy-0.2.0.dist-info → mimicpy-0.3.0.dist-info/licenses}/COPYING.LESSER +0 -0
  52. {mimicpy-0.2.0.dist-info → mimicpy-0.3.0.dist-info}/top_level.txt +0 -0
  53. {mimicpy-0.2.0.dist-info → mimicpy-0.3.0.dist-info}/zip-safe +0 -0
mimicpy/topology/mpt.py CHANGED
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # MiMiCPy: Python Based Tools for MiMiC
3
- # Copyright (C) 2020-2021 Bharath Raghavan,
3
+ # Copyright (C) 2020-2023 Bharath Raghavan,
4
4
  # Florian Schackert
5
5
  #
6
6
  # This file is part of MiMiCPy.
mimicpy/topology/top.py CHANGED
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # MiMiCPy: Python Based Tools for MiMiC
3
- # Copyright (C) 2020-2021 Bharath Raghavan,
3
+ # Copyright (C) 2020-2023 Bharath Raghavan,
4
4
  # Florian Schackert
5
5
  #
6
6
  # This file is part of MiMiCPy.
@@ -24,6 +24,7 @@
24
24
  import logging
25
25
  from os import environ
26
26
  from os.path import basename, join, isfile
27
+ from pathlib import Path
27
28
  import pandas as pd
28
29
  from .itp import Itp
29
30
  from .topol_dict import TopolDict
@@ -66,7 +67,8 @@ class Top:
66
67
  self.molecules = None
67
68
  self.bonds = None
68
69
  self.topol_dict = None
69
-
70
+ self.mol_offsets = None
71
+ self.nrexcl_values = {} # Store nrexcl values for all molecules
70
72
  if mode == 'r':
71
73
  self.__read()
72
74
  elif mode == 'w':
@@ -82,13 +84,20 @@ class Top:
82
84
  self.atomtypes = top.atom_types_df
83
85
  if get_only_atomtypes: return
84
86
  molecule_types = top.molecule_types
85
-
86
87
  if self.nonstandard_atomtypes is not None:
87
88
  atom_types.update(self.nonstandard_atomtypes)
88
-
89
+
90
+ self.atom_types_df = pd.DataFrame()
89
91
  atoms = {}
90
92
  bonds = {}
93
+ angles = {}
94
+ dihedrals = {}
95
+ pairs = {}
91
96
  guessed_elems_history = {}
97
+ source_files = {} # Store source file information in a dictionary
98
+ nrexcl_values = {} # Store nrexcl values from all ITP files
99
+ force_fields = {} # Store force field data
100
+ parameter_definitions = {} # Store parameter definitions (#define statements)
92
101
 
93
102
  for itp_file in top.topology_files:
94
103
  itp_file_name = basename(itp_file)
@@ -99,22 +108,84 @@ class Top:
99
108
  self.buffer,
100
109
  'r',
101
110
  self.guess_elements,
102
- self.gmxdata)
111
+ self.gmxdata,
112
+ parameter_definitions)
113
+ _ = itp.atom_types
114
+ if not itp.atom_types_df.empty:
115
+ self.atom_types_df = pd.concat([self.atom_types_df,itp.atom_types_df], axis=0, ignore_index=True)
116
+
117
+ # Collect parameter definitions from all files (not just force field files)
118
+ if itp.parameter_definitions:
119
+ parameter_definitions.update(itp.parameter_definitions)
120
+ logging.debug('Read parameter definitions from %s.', itp_file_name)
121
+
122
+ # Check if this is a force field file (contains force field sections)
123
+ if itp.bondtypes or itp.angletypes or itp.dihedraltypes:
124
+ # Extract force field name from filename
125
+ force_field_name = Path(itp_file_name).stem
126
+ if force_field_name not in force_fields:
127
+ force_fields[force_field_name] = {
128
+ 'bondtypes': {},
129
+ 'angletypes': {},
130
+ 'dihedraltypes': {}
131
+ }
132
+ force_fields[force_field_name]['bondtypes'].update(itp.bondtypes)
133
+ force_fields[force_field_name]['angletypes'].update(itp.angletypes)
134
+ force_fields[force_field_name]['dihedraltypes'].update(itp.dihedraltypes)
135
+ logging.debug('Read force field parameters from %s.', itp_file_name)
136
+
103
137
  if itp.topol is not None:
104
138
  atoms.update(itp.topol)
105
139
  bonds.update(itp.bonds)
140
+ angles.update(itp.angles)
141
+ dihedrals.update(itp.dihedrals)
142
+ pairs.update(itp.pairs)
106
143
  guessed_elems_history.update(itp.guessed_elems_history)
144
+ # Store source file information in dictionary
145
+ for mol in itp.topol:
146
+ if hasattr(itp.topol[mol], 'source_file'):
147
+ source_files[mol] = itp.topol[mol].source_file
148
+ # Store nrexcl values
149
+ nrexcl_values.update(itp.nrexcl_values)
107
150
  logging.debug('Read atoms from %s.', itp_file_name)
108
151
  else:
109
152
  logging.debug('No atoms found in %s.', itp_file_name)
110
153
  except OSError:
111
154
  logging.warning('Could not find %s in local or GROMACS data directory. Skipping.', itp_file_name)
112
- topol_dict = TopolDict.from_dict(atoms)
113
-
155
+
114
156
  self.molecules = top.molecules
115
- self.topol_dict = topol_dict
157
+
158
+ # Create TopolDict with all collected data
159
+ self.topol_dict = TopolDict.from_dict(atoms, bonds, angles, dihedrals, pairs)
160
+ # Add source file information to TopolDict
161
+ for mol, source_file in source_files.items():
162
+ self.topol_dict.add_source_file(mol, source_file)
163
+ # Add nrexcl values to TopolDict
164
+ for mol, nrexcl in nrexcl_values.items():
165
+ self.topol_dict.add_nrexcl_value(mol, nrexcl)
166
+ # Add force field data to TopolDict
167
+ for force_field_name, ff_data in force_fields.items():
168
+ self.topol_dict.add_force_field(
169
+ force_field_name,
170
+ bondtypes=ff_data['bondtypes'],
171
+ angletypes=ff_data['angletypes'],
172
+ dihedraltypes=ff_data['dihedraltypes']
173
+ )
174
+ # Add parameter definitions to TopolDict
175
+ self.topol_dict.add_parameter_definitions(parameter_definitions)
176
+ # Store nrexcl values
177
+ self.nrexcl_values = nrexcl_values
116
178
  self.bonds = self.__get_bonds(bonds)
117
179
 
180
+ mol_offsets = []
181
+ offset = 0
182
+ for mol, n_mols in self.molecules:
183
+ mol_len = len(self.topol_dict[mol])
184
+ for i in range(n_mols):
185
+ mol_offsets.append((mol, offset))
186
+ offset += mol_len
187
+ self.mol_offsets = mol_offsets
188
+
118
189
  mols_not_in = self.topol_dict.check_mols(self.molecules)
119
190
 
120
191
  if mols_not_in:
@@ -140,19 +211,32 @@ class Top:
140
211
 
141
212
  return pd.DataFrame(bonds_section_dct)
142
213
 
143
- def write_atomtypes(self, file, delete_atomtypes=None):
214
+
215
+ def write_atomtypes(self, file=None, delete_atomtypes=None):
216
+ """Write atomtypes to a file with support for modifying multiple files
217
+
218
+ Args:
219
+ file (str, optional): Target file to write atomtypes to. If None, uses self.file
220
+ delete_atomtypes (list, optional): List of files to delete atomtypes sections from
221
+ """
222
+ if file is None:
223
+ file = self.file
224
+
144
225
  if self.mode != 'w':
145
226
  self.mode = 'w'
146
227
  self.__read(True)
147
228
 
229
+ # Get elements from topology dictionary
148
230
  elements = {}
149
231
  for k, df in self.topol_dict.todict().items():
150
232
  elements.update(dict(zip(df['type'], df['element'])))
151
233
  elements = {k:atomic_numbers[v] for k,v in elements.items()}
152
234
 
235
+ # Create atomtypes string
153
236
  itp_str = "; Created by mimicpy fixtop\n[ atomtypes ]\n"
154
- itp_str += "; {:^11}{:^6}{:^10}{:^10}{:^6} {} {}\n".format('name','at.num','mass','charge','ptype',
155
- 'sigma','epsilon')
237
+ itp_str += "; {:^11}{:^6}{:^10}{:^10}{:^6} {} {}\n".format(
238
+ 'name', 'at.num', 'mass', 'charge', 'ptype', 'sigma', 'epsilon')
239
+
156
240
  for i, row in self.atomtypes.iterrows():
157
241
  lst = [row[c] for c in self.atomtypes.columns]
158
242
  if lst[0] in elements:
@@ -161,10 +245,10 @@ class Top:
161
245
  lst[1] = int(lst[1])
162
246
  itp_str += "{:>11}{:6d}{:11.4f}{:11.4f}{:>6} {:e} {:e}\n".format(*lst)
163
247
 
164
- import re
165
- r = re.compile(r"(\[\s*atomtypes\s*\]\n(?:.+\n)+?)\s*(?:$|\[|#)", re.MULTILINE)
166
-
248
+ # Handle deletion of atomtypes sections from other files
167
249
  if delete_atomtypes is not None:
250
+ import re
251
+ r = re.compile(r"(\[\s*atomtypes\s*\]\n(?:.+\n)+?)\s*(?:$|\[|#)", re.MULTILINE)
168
252
  for i in delete_atomtypes:
169
253
  txt = read(i)
170
254
  atm_typs = r.findall(txt)
@@ -173,6 +257,7 @@ class Top:
173
257
  write(txt, i)
174
258
  logging.info('Deleted atomtypes sections from %s', ', '.join(delete_atomtypes))
175
259
 
260
+ # Write to target file
176
261
  if isfile(file):
177
262
  txt = read(file)
178
263
  atm_typs = r.findall(txt)
@@ -183,4 +268,158 @@ class Top:
183
268
  raise FileExistsError('%s exists and has no [ atomtypes ] section to replace', file)
184
269
  else:
185
270
  write(itp_str, file)
186
- logging.info('Fixed [ atomtypes ] section and wrote to %s', file)
271
+ logging.info('Fixed [ atomtypes ] section and wrote to %s', file)
272
+
273
+ def write_topology(self, output_file):
274
+ """Write complete topology to file including all interactions
275
+
276
+ Args:
277
+ output_file (str): Path to output file
278
+ """
279
+
280
+ # Write system name
281
+ top_str = "; Created by mimicpy\n"
282
+ top_str += "[ system ]\n"
283
+ top_str += "; Name\n"
284
+ top_str += "System\n\n"
285
+
286
+ # Write molecules section
287
+ top_str += "[ molecules ]\n"
288
+ top_str += "; Compound #mols\n"
289
+ for mol, n_mols in self.molecules:
290
+ top_str += f"{mol:16s} {n_mols:6d}\n"
291
+ top_str += "\n"
292
+
293
+ # Write atomtypes section
294
+ top_str += "[ atomtypes ]\n"
295
+ top_str += "; {:^11}{:^6}{:^10}{:^10}{:^6} {} {}\n".format(
296
+ 'name', 'at.num', 'mass', 'charge', 'ptype', 'sigma', 'epsilon')
297
+
298
+ # Get unique atom types from topology
299
+ atom_types = set()
300
+ for mol in self.topol_dict.keys():
301
+ atom_types.update(self.topol_dict[mol]['type'].unique())
302
+
303
+ for atom_type in sorted(atom_types):
304
+ row = self.atom_types_df.loc[atom_type]
305
+ top_str += "{:>11}{:6d}{:11.4f}{:11.4f}{:>6} {:e} {:e}\n".format(
306
+ row['type'], row['X'], row['mass'], row['charge'],
307
+ row['ptype'], row['sigma'], row['epsilon'])
308
+ top_str += "\n"
309
+
310
+ # Write molecule types and their interactions to separate .itp files
311
+ for mol, n_mols in self.molecules:
312
+ # Create .itp file for this molecule
313
+ itp_file = f"{mol}.itp"
314
+
315
+ # Get nrexcl value for this molecule
316
+ nrexcl = self.topol_dict.get_nrexcl_value(mol)
317
+
318
+ itp_str = f"; Created by mimicpy\n[ moleculetype ]\n; Name nrexcl\n{mol:16s} {nrexcl}\n\n"
319
+
320
+ # Write atoms section
321
+ itp_str += "[ atoms ]\n"
322
+ itp_str += "; nr type resnr residu atom cgnr charge mass\n"
323
+ atoms_df = self.topol_dict[mol]
324
+ for idx, row in atoms_df.iterrows():
325
+ itp_str += f"{idx:6d} {row['type']:8s} {row['resid']:6d} {row['resname']:8s} {row['name']:8s} {row['charge']:8.4f} {row['mass']:12.4f}\n"
326
+ itp_str += "\n"
327
+
328
+ # Get interactions for this molecule
329
+ mol_bonds = self.topol_dict.get_bonds(mol)
330
+ mol_angles = self.topol_dict.get_angles(mol)
331
+ mol_dihedrals = self.topol_dict.get_dihedrals(mol)
332
+ mol_pairs = self.topol_dict.get_pairs(mol)
333
+ # Write bonds
334
+ if mol_bonds:
335
+ itp_str += "[ bonds ]\n"
336
+ itp_str += "; ai aj funct param1 param2\n"
337
+ for j in range(len(mol_bonds[0])):
338
+ i_idx = mol_bonds[0][j]
339
+ j_idx = mol_bonds[1][j]
340
+ func = mol_bonds[2][j]
341
+ p1 = mol_bonds[3][j]
342
+ p2 = mol_bonds[4][j]
343
+ itp_str += f"{i_idx:6d} {j_idx:6d} {func:6d} {p1:10.5E} {p2:10.5E}\n"
344
+ itp_str += "\n"
345
+
346
+ # Write pairs
347
+ if mol_pairs:
348
+ itp_str += "[ pairs ]\n"
349
+ itp_str += "; ai aj funct\n"
350
+ for j in range(len(mol_pairs[0])):
351
+ i_idx = mol_pairs[0][j]
352
+ j_idx = mol_pairs[1][j]
353
+ func = mol_pairs[2][j]
354
+ itp_str += f"{i_idx:6d} {j_idx:6d} {func:6d}\n"
355
+ itp_str += "\n"
356
+ # Write angles
357
+ if mol_angles:
358
+ itp_str += "[ angles ]\n"
359
+ itp_str += "; ai aj ak funct param1 param2\n"
360
+ for j in range(len(mol_angles[0])):
361
+ i_idx = mol_angles[0][j]
362
+ j_idx = mol_angles[1][j]
363
+ k_idx = mol_angles[2][j]
364
+ func = mol_angles[3][j]
365
+ p1 = mol_angles[4][j]
366
+ p2 = mol_angles[5][j]
367
+ itp_str += f"{i_idx:6d} {j_idx:6d} {k_idx:6d} {func:6d} {p1:10.5E} {p2:10.5E}\n"
368
+ itp_str += "\n"
369
+
370
+ # Write dihedrals
371
+ if mol_dihedrals:
372
+ # Group dihedrals by function type
373
+ dihedrals_by_func = {}
374
+ for j in range(len(mol_dihedrals[0])):
375
+ func = mol_dihedrals[4][j]
376
+ if func not in dihedrals_by_func:
377
+ dihedrals_by_func[func] = []
378
+ dihedrals_by_func[func].append(j)
379
+
380
+ # Write each function type in a separate section
381
+ for func, indices in dihedrals_by_func.items():
382
+ itp_str += "[ dihedrals ]\n"
383
+ # Write header based on function type
384
+ if func in [1, 4, 9]: # Format 1
385
+ itp_str += "; ai aj ak al funct phi0 cp mult\n"
386
+ elif func == 2: # Format 2
387
+ itp_str += "; ai aj ak al funct param1 param2\n"
388
+ elif func == 3: # Format 3
389
+ itp_str += "; ai aj ak al funct C0 C1 C2 C3 C4 C5\n"
390
+
391
+ for j in indices:
392
+ i_idx = mol_dihedrals[0][j]
393
+ j_idx = mol_dihedrals[1][j]
394
+ k_idx = mol_dihedrals[2][j]
395
+ l_idx = mol_dihedrals[3][j]
396
+
397
+ if func in [1, 4, 9]: # Format 1
398
+ phi0 = mol_dihedrals[5][j]
399
+ cp = mol_dihedrals[6][j]
400
+ mult = mol_dihedrals[7][j]
401
+ itp_str += f"{i_idx:6d} {j_idx:6d} {k_idx:6d} {l_idx:6d} {func:6d} {phi0:8.1f} {cp:8.2f} {mult:6d}\n"
402
+ elif func == 2: # Format 2
403
+ p1 = mol_dihedrals[8][j]
404
+ p2 = mol_dihedrals[9][j]
405
+ itp_str += f"{i_idx:6d} {j_idx:6d} {k_idx:6d} {l_idx:6d} {func:6d} {p1:8.3f} {p2:10.5E}\n"
406
+ elif func == 3: # Format 3
407
+ c0, c1, c2, c3, c4, c5 = [mol_dihedrals[k][j] for k in range(10, 16)]
408
+ itp_str += f"{i_idx:6d} {j_idx:6d} {k_idx:6d} {l_idx:6d} {func:6d} {c0:8.5f} {c1:8.5f} {c2:8.5f} {c3:8.5f} {c4:8.5f} {c5:8.5f}\n"
409
+ itp_str += "\n"
410
+
411
+ # Write the .itp file
412
+ with open(itp_file, 'w') as f:
413
+ f.write(itp_str)
414
+
415
+ # Include the .itp file in the main topology
416
+ top_str += f'#include "{itp_file}"\n'
417
+
418
+ # Write the main topology file
419
+ with open(output_file, 'w') as f:
420
+ f.write(top_str)
421
+
422
+ logging.info(f'Successfully wrote topology to {output_file}')
423
+ return True
424
+
425
+
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # MiMiCPy: Python Based Tools for MiMiC
3
- # Copyright (C) 2020-2021 Bharath Raghavan,
3
+ # Copyright (C) 2020-2023 Bharath Raghavan,
4
4
  # Florian Schackert
5
5
  #
6
6
  # This file is part of MiMiCPy.
@@ -21,14 +21,21 @@
21
21
 
22
22
  """Module for MiMiCPy-specific molecule:topology dictionary"""
23
23
 
24
+ import logging
25
+
24
26
 
25
27
  class TopolDict:
26
28
  """provides a dictionary with non-repeating topology information"""
27
29
 
28
30
  @classmethod
29
- def from_dict(cls, dict_df):
31
+ def from_dict(cls, dict_df, bonds=None, angles=None, dihedrals=None, pairs=None):
30
32
  keys = list(dict_df.keys())
31
33
  repeating = {}
34
+ repeating_bonds = {}
35
+ repeating_angles = {}
36
+ repeating_dihedrals = {}
37
+ repeating_pairs = {}
38
+
32
39
  i = 0
33
40
  while i < len(keys):
34
41
  key_i = keys[i]
@@ -36,15 +43,209 @@ class TopolDict:
36
43
  key_j = keys[j]
37
44
  if dict_df[key_i].equals(dict_df[key_j]):
38
45
  repeating[key_j] = key_i
46
+ if bonds and key_j in bonds:
47
+ repeating_bonds[key_j] = key_i
48
+ if angles and key_j in angles:
49
+ repeating_angles[key_j] = key_i
50
+ if dihedrals and key_j in dihedrals:
51
+ repeating_dihedrals[key_j] = key_i
52
+ if pairs and key_j in pairs:
53
+ repeating_pairs[key_j] = key_i
39
54
  del dict_df[key_j]
40
55
  i += 1
41
56
  keys = list(dict_df.keys())
42
- return cls(dict_df, repeating)
57
+ return cls(dict_df, repeating, bonds, angles, dihedrals, pairs,
58
+ repeating_bonds, repeating_angles, repeating_dihedrals, repeating_pairs)
43
59
 
44
- def __init__(self, dict_df, repeating):
60
+ def __init__(self, dict_df, repeating, bonds=None, angles=None, dihedrals=None, pairs=None,
61
+ repeating_bonds=None, repeating_angles=None, repeating_dihedrals=None, repeating_pairs=None):
45
62
  self.dict_df = dict_df
46
63
  self.repeating = repeating
64
+ self.bonds = bonds or {}
65
+ self.angles = angles or {}
66
+ self.dihedrals = dihedrals or {}
67
+ self.pairs = pairs or {}
68
+ self.repeating_bonds = repeating_bonds or {}
69
+ self.repeating_angles = repeating_angles or {}
70
+ self.repeating_dihedrals = repeating_dihedrals or {}
71
+ self.repeating_pairs = repeating_pairs or {}
72
+ # Add source file tracking
73
+ self.source_files = {}
74
+ self.nrexcl_values = {} # Store nrexcl values for molecules
75
+ self.mol_num_atoms = {mol: len(self.dict_df[mol]) for mol in self.dict_df}
76
+ # Add force field storage
77
+ self.force_fields = {} # Store force field parameters by force field name
78
+ self.parameter_definitions = {} # Store parameter definitions (#define statements)
79
+
80
+ def add_source_file(self, mol_name, source_file):
81
+ """Add source file information for a molecule
82
+
83
+ Args:
84
+ mol_name (str): Name of the molecule
85
+ source_file (str): Path to the source file
86
+ """
87
+ self.source_files[mol_name] = source_file
88
+
89
+ def get_source_file(self, mol_name):
90
+ """Get source file for a molecule
91
+
92
+ Args:
93
+ mol_name (str): Name of the molecule
94
+
95
+ Returns:
96
+ str: Path to the source file, or None if not found
97
+ """
98
+ if mol_name in self.source_files:
99
+ return self.source_files[mol_name]
100
+ if mol_name in self.repeating:
101
+ return self.source_files.get(self.repeating[mol_name])
102
+ return None
103
+
104
+ def add_nrexcl_value(self, mol_name, nrexcl):
105
+ """Add nrexcl value for a molecule
106
+
107
+ Args:
108
+ mol_name (str): Name of the molecule
109
+ nrexcl (int): nrexcl value for the molecule
110
+ """
111
+ self.nrexcl_values[mol_name] = nrexcl
112
+
113
+ def get_nrexcl_value(self, mol_name):
114
+ """Get nrexcl value for a molecule
115
+
116
+ Args:
117
+ mol_name (str): Name of the molecule
118
+
119
+ Returns:
120
+ int: nrexcl value for the molecule, or 3 if not found (default)
121
+ """
122
+ if mol_name in self.nrexcl_values:
123
+ return self.nrexcl_values[mol_name]
124
+ if mol_name in self.repeating:
125
+ return self.nrexcl_values.get(self.repeating[mol_name], 3)
126
+ return 3 # Default value
127
+
128
+ def add_force_field(self, force_field_name, bondtypes=None, angletypes=None, dihedraltypes=None):
129
+ """Add force field parameters to the topology dictionary
130
+
131
+ Args:
132
+ force_field_name (str): Name of the force field
133
+ bondtypes (dict): Bond type parameters
134
+ angletypes (dict): Angle type parameters
135
+ dihedraltypes (dict): Dihedral type parameters
136
+ """
137
+ self.force_fields[force_field_name] = {
138
+ 'bondtypes': bondtypes or {},
139
+ 'angletypes': angletypes or {},
140
+ 'dihedraltypes': dihedraltypes or {}
141
+ }
142
+
143
+ def get_force_field(self, force_field_name):
144
+ """Get force field parameters by name
145
+
146
+ Args:
147
+ force_field_name (str): Name of the force field
148
+
149
+ Returns:
150
+ dict: Force field parameters or None if not found
151
+ """
152
+ return self.force_fields.get(force_field_name)
153
+
154
+ def get_force_field_bondtypes(self, force_field_name):
155
+ """Get bond types for a specific force field
156
+
157
+ Args:
158
+ force_field_name (str): Name of the force field
159
+
160
+ Returns:
161
+ dict: Bond type parameters or empty dict if not found
162
+ """
163
+ ff = self.get_force_field(force_field_name)
164
+ return ff.get('bondtypes', {}) if ff else {}
165
+
166
+ def get_force_field_angletypes(self, force_field_name):
167
+ """Get angle types for a specific force field
168
+
169
+ Args:
170
+ force_field_name (str): Name of the force field
171
+
172
+ Returns:
173
+ dict: Angle type parameters or empty dict if not found
174
+ """
175
+ ff = self.get_force_field(force_field_name)
176
+ return ff.get('angletypes', {}) if ff else {}
47
177
 
178
+ def get_force_field_dihedraltypes(self, force_field_name):
179
+ """Get dihedral types for a specific force field
180
+
181
+ Args:
182
+ force_field_name (str): Name of the force field
183
+
184
+ Returns:
185
+ dict: Dihedral type parameters or empty dict if not found
186
+ """
187
+ ff = self.get_force_field(force_field_name)
188
+ return ff.get('dihedraltypes', {}) if ff else {}
189
+
190
+ def list_force_fields(self):
191
+ """Get list of available force fields
192
+
193
+ Returns:
194
+ list: List of force field names
195
+ """
196
+ return list(self.force_fields.keys())
197
+
198
+ def add_parameter_definitions(self, parameter_definitions):
199
+ """Add parameter definitions (#define statements) to the topology dictionary
200
+
201
+ Args:
202
+ parameter_definitions (dict): Dictionary of parameter definitions
203
+ """
204
+ self.parameter_definitions.update(parameter_definitions)
205
+
206
+ def get_parameter_definitions(self):
207
+ """Get all parameter definitions
208
+
209
+ Returns:
210
+ dict: Dictionary of parameter definitions
211
+ """
212
+ return self.parameter_definitions
213
+
214
+ def resolve_parameter_reference(self, param_ref):
215
+ """Resolve a parameter reference to its actual values
216
+
217
+ Args:
218
+ param_ref (str): Parameter reference name (e.g., 'torsion_ASN_CA_CB_CG_ND2_mult1')
219
+
220
+ Returns:
221
+ list: List of parameter values or None if not found
222
+ """
223
+ if param_ref in self.parameter_definitions:
224
+ param_str = self.parameter_definitions[param_ref]
225
+ try:
226
+ # Remove comments and parse the parameter values (e.g., "0.0 -4.376464 1")
227
+ # Split by semicolon and take only the first part
228
+ clean_param_str = param_str.split(';')[0].strip()
229
+ values = [float(x) for x in clean_param_str.split()]
230
+ return values
231
+ except ValueError:
232
+ logging.warning(f'Could not parse parameter definition for {param_ref}: {param_str}')
233
+ return None
234
+ return None
235
+
236
+ def get_num_atoms(self, mol_name):
237
+ """Get the number of atoms in a molecule
238
+
239
+ Args:
240
+ mol_name (str): Name of the molecule
241
+
242
+ Returns:
243
+ int: Number of atoms in the molecule
244
+ """
245
+ if mol_name not in self.mol_num_atoms:
246
+ raise KeyError('Molecule {} is not in topology'.format(mol_name))
247
+ return self.mol_num_atoms[mol_name]
248
+
48
249
  def __getitem__(self, key):
49
250
  if key in self.dict_df:
50
251
  return self.dict_df[key]
@@ -52,6 +253,34 @@ class TopolDict:
52
253
  return self.dict_df[self.repeating[key]]
53
254
  raise KeyError('Molecule {} is not in topology'.format(key))
54
255
 
256
+ def get_bonds(self, key):
257
+ if key in self.bonds:
258
+ return self.bonds[key]
259
+ if key in self.repeating_bonds:
260
+ return self.bonds[self.repeating_bonds[key]]
261
+ return None
262
+
263
+ def get_angles(self, key):
264
+ if key in self.angles:
265
+ return self.angles[key]
266
+ if key in self.repeating_angles:
267
+ return self.angles[self.repeating_angles[key]]
268
+ return None
269
+
270
+ def get_dihedrals(self, key):
271
+ if key in self.dihedrals:
272
+ return self.dihedrals[key]
273
+ if key in self.repeating_dihedrals:
274
+ return self.dihedrals[self.repeating_dihedrals[key]]
275
+ return None
276
+
277
+ def get_pairs(self, key):
278
+ if key in self.pairs:
279
+ return self.pairs[key]
280
+ if key in self.repeating_pairs:
281
+ return self.pairs[self.repeating_pairs[key]]
282
+ return None
283
+
55
284
  def __contains__(self, key):
56
285
  return key in self.dict_df or key in self.repeating
57
286
 
mimicpy/utils/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # MiMiCPy: Python Based Tools for MiMiC
3
- # Copyright (C) 2020-2021 Bharath Raghavan,
3
+ # Copyright (C) 2020-2023 Bharath Raghavan,
4
4
  # Florian Schackert
5
5
  #
6
6
  # This file is part of MiMiCPy.
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # MiMiCPy: Python Based Tools for MiMiC
3
- # Copyright (C) 2020-2021 Bharath Raghavan,
3
+ # Copyright (C) 2020-2023 Bharath Raghavan,
4
4
  # Florian Schackert
5
5
  #
6
6
  # This file is part of MiMiCPy.
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # MiMiCPy: Python Based Tools for MiMiC
3
- # Copyright (C) 2020-2021 Bharath Raghavan,
3
+ # Copyright (C) 2020-2023 Bharath Raghavan,
4
4
  # Florian Schackert
5
5
  #
6
6
  # This file is part of MiMiCPy.
@@ -19,5 +19,19 @@
19
19
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
20
  #
21
21
 
22
- BOHR_RADIUS = 0.0529177210903 # in nm
23
- ATOMIC_TIME_UNIT = 2.4188843265857E-5 # in ps
22
+
23
+ BOHR_RADIUS = 5.29177210859e-02 # in nm
24
+ ATOMIC_TIME_UNIT = 2.4188843265864E-5 # in ps
25
+
26
+ au_kjm=2.62549962505e+3
27
+ kjm_au = 1 / au_kjm
28
+
29
+ au_to_nm = BOHR_RADIUS
30
+ nm_to_au = 1 / au_to_nm
31
+ au_to_kjmolnm = au_kjm / au_to_nm
32
+ kjmolnm_to_au = 1 / au_to_kjmolnm
33
+ kb_gmx2au = kjm_au / nm_to_au**2
34
+ kb_au2gmx = 1/ kb_gmx2au
35
+ kb_g962au = kjm_au / nm_to_au**4
36
+ kb_au2g96 = 1/ kb_g962au
37
+ electric_conversion_factor = 138.935458