Graph

This section lists functions that work on the system Graph, such as functions that deal with parent/child relations, indexes and bonds, among others. These are subdivided by topics, for organization purposes:

Root vs Origin

The root of a Graph is a set of 3 pseudoatoms belonging to a Topology, acting as the anchor for internal coordinate calculations, while an origin is any Atom instance with no parent in any given container (for example, due to a cut). While a Pose can only have 1 root, it can have multiple origins (which usually need to be reconnected for most of the simulations of ProtoSyn).

ProtoSyn.originFunction
origin(container::AbstractContainer)

Return the first Atom in AbstractContainer container that has no parent. The iteration follows the Atom instance :id field, if correctly indexed. If no Atom instance without parent is found (i.e.: circular structures), return nothing. Note that the root atoms are not considered. Note that if multiple origin Atom instances exists, this method return only the first found, based on the current Atom order in the given AbstractContainer container.

See also

root reindex sort_atoms_by_graph!

source
ProtoSyn.rootFunction
root(container::AbstractContainer)

Return the first Atom of the Root of the Graph that given AbstractContainer container belongs to. If the given AbstractContainer container is not a Topology instance and has :container field set to nothing, return nothing.

root(topology::Topology)

Return the first Atom of the Root of the given Topology topology instance.

See also

origin

source
ProtoSyn.RootFunction
Root()::Residue

Return a new Root residue. A Root residue in a pseudo-residue that serves the purpose of establishing an anchor for initial internal coordinates definition.

Examples

julia> root = ProtoSyn.Root()
Residue{/ROOT:17124}
source

Parenthood relationships

Atom and Residue instances have parenthood relationships, in a directional Graph. Several methods allow the manipulation of such relationships, and are of additional importance when using internal coordinates to facilitate and speed up some calculations.

ProtoSyn.setparent!Function
setparent!(child::T, parent::T) where {T <: AbstractContainer}

Set parent as the parent of child, while adding child to parent.children.

See also

popparent!

source
ProtoSyn.popparent!Function
popparent!(child::AbstractContainer}

Remove the parent from child (sets it to nothing) while removing child from parent.children (only if child is a child of parent).

See also

setparent!

source
ProtoSyn.ascendentsFunction
ascedents(container::AbstractContainer, level::Int)

Return a Tuple containing the N (level) previous :id fields of the :parent AbstractContainer instances of the given container (recursivelly).

Examples

julia> ascendents(pose.graph[1][1][4], 4)
(4, 3, 1, 0)
source
Base.detachFunction
detach(segment::Segment)

Detach and return the given Segment from it's container Graph, by:

  • Detaching any Atom and Residue instance from the Graph's Root (by popping parenthood relationships), if said instances belong to the given Segment instance.
  • Deleting this Segment from its container Topology.

This function is a Base module overload.

Note:

This function does not alter the State of the Pose containing the provided Segment.

Examples

julia> detach(pose.graph[1])
Segment{/UNK:1}
source
ProtoSyn.is_contiguousFunction
is_contiguous(pose::Pose, selection::AbstractSelection)

Returns true if all the Residue instances gathered from the selection applied to the given pose are contiguous (have a parenthood relationship connecting them all). Note that the given selection is always promoted to Residue level.

See also

ProtoSyn.promote

Examples

julia> ProtoSyn.is_contiguous(pose, rid"1" | rid"3")
false

julia> ProtoSyn.is_contiguous(pose, rid"1:10")
true
source
ProtoSyn.infer_parenthood!Function
infer_parenthood!(container::ProtoSyn.AbstractContainer; overwrite::Bool = false, start::Opt{Atom} = nothing)

Infers parenthood of Atom instances on the given AbstractContainer container, from bond information, using a custom algorithm similar to breath first algorithm (atoms are sorted based on the size of the downstream graph and aromaticity). By default, the Graph origin is set to the first Atom instance in the container. This behaviour can be controlled by setting a start Atom as the origin of the new infered parenthood Graph. If overwrite is set to true (false, by default), will overwrite existing pranthood information. After infering parenthood, if changes to the Graph occurred, the existing internal coordinates match different cartesian coordinates. It's suggested to update internal coordinates (request_i2c! & sync!). For more details, see the Travelling the Graph section. If the linear_aromatics flag is set to true (is, by default), aromatic rings are treated as isolated structures is an otherwise linear Graph (for example, in some protein aminoacids). More complex structures (such as carbon sheets) have interlaced aromatic rings, and the linear_aromatics should be set to false to ensure all Atom instances are visited.

See also

travel_bonds infer_bonds!

Examples

julia> ProtoSyn.infer_parenthood!(pose.graph[1], overwrite = true)
Segment{/2a3d:8625/A:1}
source

ProtoSyn graph

Figure 1 | A diagram of a directional Graph in ProtoSyn.

Container manipulation

Atom instances have a :container field, setting the container AbstractContainer (usually a Residue instance). The same logic applies to Residue instances, inside Segment structs, and Segment instances inside Topology structs (according to the established hierarchy of AbstractContainer instances). The following methods allow for the correct manipulation of this relations, allowing to add and remove AbstractContainer instances from the respective :container field, as well as creating copies of them.

ProtoSyn.hascontainerFunction
hascontainer(c::AbstractContainer)

Return true if the given AbstractContainer.container is not nothing.

source
Base.push!Method
push!(container::AbstractContainer{T}, item::T) where {T <: AbstractContainer}

Add an AbstractContainer item to the AbstractContainer container, updating the container size and setting the correct item.container. Return the altered container.

push!(residue::Residue, atom::Atom)

In the specific case of adding an Atom atom to a Residue residue, also add the atom.name to the residue.itemsbyname dictionary for correct indexation by name. Return the altered Residue residue.

push!(container::AbstractContainer{T}, items::Vector{T}) where {T <: AbstractContainer}

Add a vector of AbstractContainer items in the AbstractContainer container, updating the container size and setting the correct item.container for each item in the items. Note that this method keeps the order of items. Return the altered container.

This function is a Base module overload.

Note:

This function does not set any Bonds or Parenthood relationships to other items in the same container. This function does not set a complementary State.

See also

insert! delete!

Examples

julia> push!(pose.graph[1][1], Atom("CA", -1, -1, "C"))
Residue{/UNK:1/UNK:1/SER:1}
source
Base.insert!Method
insert!(container::AbstractContainer{T}, index::Integer, item::T) where {T <: AbstractContainer}

Insert an AbstractContainer item in the AbstractContainer container at the given index, updating the container size and setting the correct item.container. Return the altered container.

insert!(container::Residue, index::Integer, item::Atom)

In the specific case of inserting an Atom atom in a Residue residue, also add the atom.name to the residue.itemsbyname dictionary for correct indexation by name. Return the altered Residue residue.

insert!(container::AbstractContainer{T}, index::Integer, items::Vector{T}) where {T <: AbstractContainer}

Insert a vector of AbstractContainer items in the AbstractContainer container at the given index, updating the container size and setting the correct item.container for each item in the items. Note that this method keeps the order of items. Return the altered container.

This function is a Base module overload.

Note:

This function does not set any Bonds or Parenthood relationships to other items in the same container. This function does not set a complementary State.

See also

push! delete!

Examples

julia> insert!(pose.graph[1][1], 3, Atom("CA", -1, -1, "C"))
Residue{/UNK:1/UNK:1/SER:1}
source
Base.delete!Method
delete!(container::AbstractContainer{T}, item::T) where {T <: AbstractContainer}

Delete the given AbstractContainer item from the AbstractContainer container, if found, while updating the container.size and item.container fields. In the specific case of deleting an Atom instance from a Residue, update container.itemsbyname as well. Return the altered container. If the given AbstractContainer item is not found in the AbstractContainer container, return nothing.

See also

push! insert!

Examples

julia> delete!(pose.graph[1][1], pose.graph[1][1]["CA"])
Residue{/UNK:1/UNK:1/SER:1}
source
Base.copyMethod
copy(container::AbstractContainer)

Return a copy of the given AbstractContainer container. Note that for Residue instances only the intra-residue Bonds and Parenthood relationships can be copied. The same logic applies to the intra-segment Graph.

Example

julia> copy(pose.graph[1, 1, 1])
Atom{/N:1}

julia> copy(pose.graph[1, 1])
Residue{/SER:1}
source
ProtoSyn.rename!Function
rename!(atom::Atom, name::String; force_rename::Bool = false)

Rename the selected Atom instance to the given name. Also updates the Atom container :itemsbyname field. If force_rename is set to true (false, by default), will change existing Atom instances with the same name trying to be introduced to adopt a temporary name (current name with "_o" appendix).

Examples

julia> ProtoSyn.rename!(pose.graph[1][1]["N"], "N1")
Atom{/2a3d:51894/A:1/MET:1/N1:1}
source
ProtoSyn.Atom!Function
Atom!(r::Residue, name::String, id::Int, index::Int, symbol::String)

Create a new Atom (with the given name, id, index and symbol) and add it to the given Residue residue. Returns the created Atom instance.

Examples

julia> atom = Atom!(pose.graph[1][1], "H1", 1, 1, "H")
Atom{/UNK:1/UNK:1/SER:1/H1:1}
source
ProtoSyn.Residue!Function
Residue!(segment::Segment, name::String, id::Int)::Residue

Create a new Residue (with the given name and id) and add it to the given Segment segment. Returns the created Residue instance.

Examples

julia> res = Residue!(pose.graph[1], "ALA", 1)
Residue{/UNK:1/UNK:1/ALA:1}
source
ProtoSyn.Segment!Function
Segment!(topology::Topology, name::String, id::Int)::Residue

Create a new Segment (with the given name and id) and add it to the given Topology topology. Returns the created Segment instance.

Examples

julia> seg = Segment!(pose.graph, "UNK", 1)
Segment{/UNK:1/UNK:1}
source

Indexation

An important initial detail when describing the Graph methods is describing the family of getindex methods overloaded by ProtoSyn when dealing with Graph structures. There is, in essence, 4 ways to access a specific instance in the Graph:

  • Using the regular syntax;
julia> pose.graph.items[1].items[1].items[1]Atom{/UNK:1/UNK:1/GLY:1/N:1}
julia> pose.graph.items[1].items[1]Residue{/UNK:1/UNK:1/GLY:1}
  • Using the short syntax with index;
julia> pose.graph[1][1][1]Atom{/UNK:1/UNK:1/GLY:1/N:1}
julia> pose.graph[1][1]Residue{/UNK:1/UNK:1/GLY:1}
  • Using the condensed syntax with index;
julia> pose.graph[1, 1, 1]Atom{/UNK:1/UNK:1/GLY:1/N:1}
julia> pose.graph[1, 1]Residue{/UNK:1/UNK:1/GLY:1}
  • Using the short or condensed syntax with atom name (for Atom instances only).
julia> pose.graph[1][1]["N"]Atom{/UNK:1/UNK:1/GLY:1/N:1}
julia> pose.graph[1, 1, "N"]Atom{/UNK:1/UNK:1/GLY:1/N:1}

Note that queries by Atom.name are case sensitive. Besides using it to get Graph structures, Atom.name fields can also be used to query for existence:

julia> "N" in pose.graph[1][1]true

The following methods deal with the correct indexation of the Graph. Note that, altough not necessary, some simulation functions assume that both the Graph and State indexation of a Pose are synched and are equal.

ProtoSyn.reindexMethod
reindex(topology::Topology; set_ascendents = true)

Re-indexes the whole Topology topology, setting both the :id and :index of instances inside the topology to the corresponding relative index in the container.items which they belong to. If set_ascendents is set to true (is, by default), each Atom instance :ascendents field will be updated to reflect the new indices.

reindex(segment::Segment)

Re-indexes a Segment segment, setting both the :id and :index of instances inside the topology to the corresponding relative index in the container.items which they belong to.

See also

ascendents reindex(::State)

Examples

julia> reindex(pose.graph)
Topology{/UNK:1}

julia> reindex(pose.graph[1])
Segment{/UNK:1/UNK:1}
source
ProtoSyn.idsFunction
ids(atoms::Vector{Atom})

Return a vector with the :id Int field for every Atom in the given atoms vector.

See also

travel_graph

Examples

julia> ProtoSyn.ids(an"CA"(pose, gather = true))
21-element Vector{Int64}:
   3
  14
  29
  40
  55
  65
   ⋮
 243
 257
 281
 300
 317
 327
source

Counters and Iterators

ProtoSyn includes custom Counters and Iterators to analyze and loop over Graph structures. In one hand, by using Counters the user can count the number of sub AbstractContainer instances in a Graph component (for example, the number of Atom instances in a Segment or a Residue). This can be achieved by using the count_atoms, count_residues and count_segments methods.

julia> ProtoSyn.count_atoms(pose.graph)39
julia> ProtoSyn.count_atoms(pose.graph[1][1])7
julia> ProtoSyn.count_residues(pose.graph)3

On the other hand, Iterators allow the user to iterate over all sub AbstractContainer instances in a Graph component, by using the eachatom, eachresidue and eachsegment methods.

julia> eachatom(pose.graph)ItemIterator{Topology, _ByAtom} with size (1, 3, 39)
julia> for residue in eachresidue(pose.graph[1]) println(residue); endResidue{/UNK:1/UNK:1/GLY:1} Residue{/UNK:1/UNK:1/MET:2} Residue{/UNK:1/UNK:1/GLU:3}

For Residue instances in specific, a more direct way to list all instances in a given Pose or AbstractContainer is to use the sequence method.

ProtoSyn.sequenceFunction
sequence(container::ProtoSyn.AbstractContainer)::String
sequence(pose::Pose)::String

Return the sequence of residues (in 1 letter code) of the given container/ Pose as a String. Checks the ProtoSyn.three_2_one dictionary for name to 1 letter code translation, uses '?' if no entry was found.

Examples

julia> ProtoSyn.Peptides.sequence(pose)
"SESEAEFKQRLAAIKTRLQAL"
source

Bonds

The following methods deal with the bonding/unbonding of atoms (and respective Parenthood relationships when using the join function).

ProtoSyn.bondFunction
bond(at1::Atom, at2::Atom)

Bond both given Atom instances (adds at2 to at1.bonds and vice-versa). Both Atom instances need to be in the same Segment.

See also

join unbond!

Examples

julia> ProtoSyn.bond(pose.graph[1][1]["C"], pose.graph[1][2]["CA"])
source
ProtoSyn.unbond!Function
unbond!(pose::Pose, at1::Atom, at2::Atom; [keep_downstream_position::Bool = true])::Pose

Return a Pose instance with both given Atom instances unbonded (removed from eachother bonds list, pops parenthood and sets the downstream Residue.parent field to be the Root of the upstream Topology). If keep_downstream_position is set to true (is, by default), the downstream Residue position is maintained (by calling request_c2i! and sync! methods).

Note:

Unbonding two atoms also removes any parenthood relationship, therefore making the returned Pose from this function un-usable without further changes (the internal coordinates graph is severed on the unbonding site).

Examples

julia> unbond!(pose, pose.graph[1][2]["C"], pose.graph[1][3]["N"])
Pose{Topology}(Topology{/UNK:1}, State{Float64}:
 Size: 343
 i2c: true | c2i: false
 Energy: Dict(:Total => Inf)
)
source
ProtoSyn.joinFunction
join(at1::Atom, at2::Atom)

Join Atom at1 with Atom at2.

join(r1::Residue, s1::String, r2::Residue, s2::String)

Join Atom named s1 from Residue r1 with Atom named s2 from Residue r2.

Bond (add eachother to other.bonds field) and set parent/children relationship of both the Atom instances and respective atom.container (Residue). Note that at2 Atom will become parent at at1 (and at2.container Residue will become parent of at1.container).

See also

bond unbond!

Examples

julia> Residue!(pose.graph[1], ProtoSyn.ResidueName("ALA"), 1);

julia> Atom!(pose.graph[1][end], "N", 1, 1, "N");

julia> ProtoSyn.join(pose.graph[1][1], "C", pose.graph[1][end], "N")
Residue{/UNK:1/UNK:1/ALA:1}
source
ProtoSyn.infer_bonds!Function
infer_bonds!(pose::Pose; [threshold::T = 0.1]) where {T <: AbstractFloat}

Infers bonds for all Atom instances of the given Pose. A new bond is assigned when a pair of Atom instances are within a given distance, as defined in ProtoSyn.Units.bond_lengths. The threshold value is multiplied by the standard bond distance and added to the comparison value to allow some leeway in the bond distance (0.1, by default).

See also

travel_bonds infer_parenthood!

Examples

julia> ProtoSyn.infer_bonds!(pose)
Pose{Topology}(Topology{/CRV:54976}, State{Float64}:
 Size: 201
 i2c: false | c2i: false
 Energy: Dict(:Total => Inf)
)
source

Travelling the Graph

As further explored in the Graph section, the directed nature of the Pose's Graph allows for easy travelling of the system. The following methods facilitate that process.

ProtoSyn.get_graph_sizeFunction
get_graph_size(atom::Atom; depth::Int = 1, max_depth::Int = 10)

Recursivelly search the the Graph starting from Atom atom (inclusive) until no children are identified or the depth > max_depth (10, by default).

Examples

julia> ProtoSyn.get_graph_size(pose.graph[1][73]["CA"])
13

julia> ProtoSyn.get_graph_size(pose.graph[1][73]["C"])
3
source
ProtoSyn.sort_childrenFunction
sort_children(atom::Atom; rev::Bool = false)

Sort the given Atom atom children, by the following criteria:

  1. By Graph size (follow Graph by employing the get_graph_size, small chains first)
  2. By Atom name (in case all children chains have the same size; alphabetical order)

By setting rev to true (false, by default), reverses the provided order.

sort_children!(atom::Atom; rev::Bool = false)

Sort the given Atom atom children and save the newly sorted childen in atom.bonds.

Examples

julia> ProtoSyn.sort_children!(pose.graph[1][72]["CA"])
3-element Vector{Atom}:
 Atom{/2a3d:31788/A:1/HIS:72/HA:1112}
 Atom{/2a3d:31788/A:1/HIS:72/CB:1113}
 Atom{/2a3d:31788/A:1/HIS:72/C:1124}
source
ProtoSyn.travel_graphFunction
travel_graph(start::Atom; [stop::Opt{Atom} = nothing], [search_algorithm::F = ProtoSyn.BFS]) where {F <: SearchAlgorithm})

Return a Vector{Atom} with all atom instances between Atom start and stop (inclusive), while following the structure's Graph. If no stop Atom instance is provided or if it isn't found as a downstream parent of the start Atom, all instances until no children Atom instances are found are returned (for example, until the end of the current Pose of Segment). By default, uses Breath First Search (BFS) algorithm (all Atom instances at the same "graph-distance" to the start Atom are consumed before the next level is considered, order is given by sort_children). Optionally, by setting search_algorithm to ProtoSyn.DFS, can employ Depth First Algorithm (DFS) (the largest chain of atom.children is recursively exhausted before consuming the smaller chains, order is given by sort_children).

See also

is_contiguous hasparent setparent!

Examples

julia> ProtoSyn.travel_graph(pose.graph[1][5]["N"], stop = pose.graph[1][6]["N"], search_algorithm = ProtoSyn.BFS)
11-element Vector{Atom}:
 Atom{/2a3d:31788/A:1/ALA:5/N:62}
 Atom{/2a3d:31788/A:1/ALA:5/H:63}
 Atom{/2a3d:31788/A:1/ALA:5/CA:64}
 Atom{/2a3d:31788/A:1/ALA:5/HA:65}
 Atom{/2a3d:31788/A:1/ALA:5/CB:66}
 Atom{/2a3d:31788/A:1/ALA:5/C:70}
 Atom{/2a3d:31788/A:1/ALA:5/HB3:69}
 Atom{/2a3d:31788/A:1/ALA:5/HB2:68}
 Atom{/2a3d:31788/A:1/ALA:5/HB1:67}
 Atom{/2a3d:31788/A:1/ALA:5/O:71}
 Atom{/2a3d:31788/A:1/GLU:6/N:72}

julia> ProtoSyn.travel_graph(pose.graph[1][5]["N"], stop = pose.graph[1][6]["N"], search_algorithm = ProtoSyn.DFS)
4-element Vector{Atom}:
 Atom{/2a3d:31788/A:1/ALA:5/N:62}
 Atom{/2a3d:31788/A:1/ALA:5/CA:64}
 Atom{/2a3d:31788/A:1/ALA:5/C:70}
 Atom{/2a3d:31788/A:1/GLU:6/N:72}
source
ProtoSyn.travel_bondsFunction
travel_graph(start::Atom; [stop::Opt{Atom} = nothing], [search_algorithm::F = ProtoSyn.BFS]) where {F <: SearchAlgorithm})

Return a Vector{Atom} with all atom instances between Atom start and stop (inclusive), while following the structure's Graph. If no stop Atom instance is provided or if it isn't found as a downstream parent of the start Atom, all instances until no children Atom instances are found are returned (for example, until the end of the current Pose of Segment). By default, uses Breath First Search (BFS) algorithm (all Atom instances at the same "graph-distance" to the start Atom are consumed before the next level is considered, order is given by sort_children). Optionally, by setting search_algorithm to ProtoSyn.DFS, can employ Depth First Algorithm (DFS) (the largest chain of atom.children is recursively exhausted before consuming the smaller chains, order is given by sort_children).

See also

is_contiguous hasparent setparent!

Examples

julia> ProtoSyn.travel_graph(pose.graph[1][5]["N"], stop = pose.graph[1][6]["N"], search_algorithm = ProtoSyn.BFS)
11-element Vector{Atom}:
 Atom{/2a3d:31788/A:1/ALA:5/N:62}
 Atom{/2a3d:31788/A:1/ALA:5/H:63}
 Atom{/2a3d:31788/A:1/ALA:5/CA:64}
 Atom{/2a3d:31788/A:1/ALA:5/HA:65}
 Atom{/2a3d:31788/A:1/ALA:5/CB:66}
 Atom{/2a3d:31788/A:1/ALA:5/C:70}
 Atom{/2a3d:31788/A:1/ALA:5/HB3:69}
 Atom{/2a3d:31788/A:1/ALA:5/HB2:68}
 Atom{/2a3d:31788/A:1/ALA:5/HB1:67}
 Atom{/2a3d:31788/A:1/ALA:5/O:71}
 Atom{/2a3d:31788/A:1/GLU:6/N:72}

julia> ProtoSyn.travel_graph(pose.graph[1][5]["N"], stop = pose.graph[1][6]["N"], search_algorithm = ProtoSyn.DFS)
4-element Vector{Atom}:
 Atom{/2a3d:31788/A:1/ALA:5/N:62}
 Atom{/2a3d:31788/A:1/ALA:5/CA:64}
 Atom{/2a3d:31788/A:1/ALA:5/C:70}
 Atom{/2a3d:31788/A:1/GLU:6/N:72}
source
ProtoSyn.identify_atom_by_bonding_patternFunction
identify_atom_by_bonding_pattern(container::AbstractContainer, pattern::Vector{String})

Returns one or more candidate Atom instances from the given AbstractContainer container that match the provided pattern (a Vector of Atom elements). This method follows the following hierarchical criteria:

Examples

julia> ProtoSyn.identify_atom_by_bonding_pattern(pose.graph[1][1], ["H", "N", "C", "C"])
3-element Vector{Atom}:
 Atom{/2a3d:3900/A:1/MET:1/H1:2}
 Atom{/2a3d:3900/A:1/MET:1/H2:3}
 Atom{/2a3d:3900/A:1/MET:1/H3:4}

julia> ProtoSyn.identify_atom_by_bonding_pattern(pose.graph[1][1], ["C", "C", "C", "C", "H"])
Atom{/2a3d:3900/A:1/MET:1/C:18}
source

As explained above, certain methods in ProtoSyn travel the directed graph. There are, however, multiple ways to follow the same graph (as longs as its ramified), depending on the criteria used on bifurcations. In the Core module, ProtoSyn makes available for the BFS (breath-first search) and DFs (depth-first search) algorithms, a type of SearchAlgorithm.

ProtoSyn.BFSConstant
(ProtoSyn.BFS)(atom::Atom, stack::Vector{Atom})

Breath first search algorithm for travel_graph. Correctly sorts the given Atom atom children instances and concatenates with the current stack.

Examples

julia> ProtoSyn.BFS(pose.graph[1][1]["CA"], Vector{Atom}())
3-element Vector{Atom}:
 Atom{/test:36441/A:1/MET:1/HA:6}
 Atom{/test:36441/A:1/MET:1/C:7}
 Atom{/test:36441/A:1/MET:1/CB:8}
source
ProtoSyn.DFSConstant
(ProtoSyn.DFS)(atom::Atom, stack::Vector{Atom})

Depth first search algorithm for travel_graph. Correctly sorts the given Atom atom children instances and concatenates with the current stack.

Examples

julia> ProtoSyn.DFS(pose.graph[1][1]["CA"], Vector{Atom}())
3-element Vector{Atom}:
 Atom{/test:36441/A:1/MET:1/CB:8}
 Atom{/test:36441/A:1/MET:1/C:7}
 Atom{/test:36441/A:1/MET:1/HA:6}
source