-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add draft for ff-output #442
base: master
Are you sure you want to change the base?
Changes from all commits
04c3beb
0ca9cc6
527f021
00f8915
d4a2ef3
6c595de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# Copyright 2020 University of Groningen | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
""" | ||
Write .ff files. | ||
|
||
The FF file format describes molecule components for a given force field. | ||
|
||
The format is built on top of a subset of the ITP format. Describing a block | ||
is done in the same way an ITP file describes a molecule. | ||
""" | ||
|
||
class ForceFieldDirectiveWriter(): | ||
""" | ||
Write force-field files according to the | ||
vermouth force-field definition. | ||
""" | ||
def __init__(self, forcefield, stream): | ||
""" | ||
Parameters | ||
---------- | ||
forcefield: `:class:vermouth.forcefield.ForceField` | ||
the force-field object to write | ||
|
||
stream: `` | ||
the stream to which to write; must have a write method | ||
""" | ||
self.forcefield = forcefield | ||
self.stream = stream | ||
# these attributes have a specific order in the moleculetype section | ||
self.normal_order_block_atoms = ["atype", "resid", "resname", | ||
"atomname", "charge_group", "charge"] #, "mass"] | ||
|
||
def write(self): | ||
""" | ||
Write the forcefield to file. | ||
""" | ||
for name, block in self.forcefield.blocks.items(): | ||
self.stream.write("[ moleculetype ]\n") | ||
excl = str(block.nrexcl) | ||
self.stream.write(f"{name} {excl}\n") | ||
self.write_atoms_block(block.nodes(data=True)) | ||
self.write_interaction_dict(block.interactions) | ||
|
||
for link in self.forcefield.links: | ||
self.write_link_header() | ||
self.write_atoms_link(link.nodes(data=True)) | ||
self.write_interaction_dict(link.interactions) | ||
self.write_edges(link.edges) | ||
|
||
def write_interaction_dict(self, inter_dict): | ||
""" | ||
Writes interactions to `self.stream`, with a new | ||
interaction directive per type. Meta attributes | ||
are kept and written as json parasable dicts. | ||
|
||
Parameters | ||
---------- | ||
inter_dict: `class:dict[list[vermouth.molecule.Interaction]]` | ||
the interaction dict to write | ||
""" | ||
for inter_type in inter_dict: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See also |
||
self.stream.write(f"[ {inter_type} ]\n") | ||
for interaction in inter_dict[inter_type]: | ||
atom_string = " ".join(interaction.atoms) | ||
param_string = " ".join(interaction.parameters) | ||
meta_string = "{" + " ,".join([f"\"{key}\": \"{value}\"" for key, value in interaction.meta.items()]) + "}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
line = atom_string + " " + param_string + " " + meta_string + "\n" | ||
self.stream.write(line) | ||
|
||
def write_edges(self, edges): | ||
""" | ||
Writes edges to `self.stream` into the edges directive. | ||
|
||
Parameters | ||
---------- | ||
edges: abc.iteratable | ||
pair-wise iteratable edge list | ||
""" | ||
self.stream.write("[ edges ]\n") | ||
for idx, jdx in edges: | ||
self.stream.write(f"{idx} {jdx}\n") | ||
Comment on lines
+82
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only write edges that are not already in interactions? |
||
|
||
def write_atoms_block(self, nodes): | ||
""" | ||
Writes the nodes/atoms of the block atomtype directive to `self.stream`. | ||
All attributes are written following the GROMACS atomtype directive | ||
style. | ||
|
||
Parameters | ||
---------- | ||
edges: abc.iteratable | ||
pair-wise iteratable edge list | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pairwise edges? |
||
""" | ||
self.stream.write("[ atoms ]\n") | ||
for idx, (node, attrs) in enumerate(nodes): | ||
idx += 1 | ||
attr_line = " ".join([str(attrs[attr]) for attr in self.normal_order_block_atoms ]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about any other attributes? |
||
line = f"{idx} " + attr_line + "\n" | ||
self.stream.write(line) | ||
|
||
def write_atoms_link(self, nodes): | ||
""" | ||
Writes the nodes/atoms of the link atomtype directive to `self.stream`. | ||
All attributes are written as json style dicts. | ||
|
||
Parameters: | ||
----------- | ||
nodes: abc.itertable[tuple(abc.hashable, dict)] | ||
list of nodes in form of a list with hashable node-key and dict | ||
of attributes. The format is the same as returned by networkx.nodes(data=True) | ||
""" | ||
self.stream.write("[ atoms ]\n") | ||
for node_key, attributes in nodes: | ||
attr_line = " {" + " ,".join([f"\"{key}\": \"{value}\"" for key, value in attributes.items()]) + "}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The attr line is defined as json, so abuse that with |
||
line = str(node_key) + attr_line + "\n" | ||
self.stream.write(line) | ||
|
||
def write_link_header(self): | ||
""" | ||
Write the link directive header, with the resnames written | ||
in form readable to geenerate a `:class:vermouth.molecule.Choice` | ||
object. | ||
|
||
Prameters | ||
--------- | ||
resnames: `abc.itertable[str]` | ||
""" | ||
self.stream.write("[ link ]\n") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stupid suggestion: how do you feel about giving blocks a str/write/repr method instead?
Also for links/modifications/...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can move this one further up the chain, give it to ForceField. Note that I'm not convinced it's a good idea though, just an idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either way, group all these into a write_block function