/
Frame.jl
303 lines (265 loc) · 8.88 KB
/
Frame.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# Chemfiles.jl, a modern library for chemistry file reading and writing
# Copyright (C) Guillaume Fraux and contributors -- BSD license
export add_atom!, remove_atom!
export has_velocities, add_velocities!, positions, velocities
export set_cell!, set_topology!, set_step!, guess_bonds!
export distance, dihedral, out_of_plane
__ptr(frame::Frame) = __ptr(frame.__handle)
__const_ptr(frame::Frame) = __const_ptr(frame.__handle)
# A small wrapper around Array{Float64, 2}, keeping a reference to the
# corresponding frame to prevent garbage collection (see issue
# https://github.com/chemfiles/Chemfiles.jl/issues/37)
struct ChemfilesArray <: AbstractArray{Float64,2}
data::Array{Float64,2}
parent::Frame
end
# Implement the Array interface for ChemfilesArray
@inline Base.size(A::ChemfilesArray) = size(A.data)
@inline Base.getindex(A::ChemfilesArray, I::Int) = getindex(A.data, I)
@inline Base.getindex(A::ChemfilesArray, I::Int...) = getindex(A.data, I...)
@inline Base.setindex!(A::ChemfilesArray, v, I::Int) = setindex!(A.data, v, I)
@inline Base.setindex!(A::ChemfilesArray, v, I::Int...) = setindex!(A.data, v, I...)
"""
Create a new empty `Frame`.
"""
function Frame()
ptr = @__check_ptr(lib.chfl_frame())
return Frame(CxxPointer(ptr, is_const=false))
end
"""
Get the number of atoms in the `frame`.
"""
function Base.size(frame::Frame)
count = Ref{UInt64}(0)
__check(lib.chfl_frame_atoms_count(__const_ptr(frame), count))
return Int(count[])
end
"""
Get the number of atoms in the `frame`.
"""
function Base.length(frame::Frame)
size(frame)
end
"""
Resize the positions and the velocities in the `frame`, to make space for
`natoms` atoms. This function may invalidate any pointer to the positions or
the velocities if the new size is bigger than the old one. In all the cases,
previous data is conserved. This function conserve the presence or absence of
velocities.
"""
function Base.resize!(frame::Frame, natoms::Integer)
__check(lib.chfl_frame_resize(__ptr(frame), UInt64(natoms)))
end
"""
Get the positions in a `Frame` as an array. The positions are readable and
writable from this array. If the frame is resized (by writing to it, or calling
`resize!`), the array is invalidated.
"""
function positions(frame::Frame)
ptr = Ref{Ptr{Float64}}()
natoms = Ref{UInt64}(0)
__check(lib.chfl_frame_positions(__ptr(frame), ptr, natoms))
data = unsafe_wrap(Array{Float64,2}, ptr[], (3, Int(natoms[])); own=false)
return ChemfilesArray(data, frame)
end
"""
Get the velocities in a `Frame` as an array. The velocities are readable and
writable from this array. If the frame is resized (by writing to it, or calling
`resize!`), the array is invalidated.
If the frame do not have velocity, this function will error. You can use
`add_velocities!` to add velocities to a frame before calling this function.
"""
function velocities(frame::Frame)
ptr = Ref{Ptr{Float64}}()
natoms = Ref{UInt64}(0)
__check(lib.chfl_frame_velocities(__ptr(frame), ptr, natoms))
data = unsafe_wrap(Array{Float64,2}, ptr[], (3, Int(natoms[])); own=false)
return ChemfilesArray(data, frame)
end
"""
Add velocities to this `frame`. The storage is initialized with the result of
`size(frame)` as the number of atoms. If the frame already has velocities, this
does nothing.
"""
function add_velocities!(frame::Frame)
__check(lib.chfl_frame_add_velocities(__ptr(frame)))
return nothing
end
"""
Check if a `frame` contains velocity data or not.
"""
function has_velocities(frame::Frame)
result = Ref{UInt8}(0)
__check(lib.chfl_frame_has_velocities(__const_ptr(frame), result))
return convert(Bool, result[])
end
"""
Set the `cell` associated with a `frame`.
"""
function set_cell!(frame::Frame, cell::UnitCell)
__check(lib.chfl_frame_set_cell(__ptr(frame), __const_ptr(cell)))
return nothing
end
"""
Set the `topology` associated with a `frame`.
"""
function set_topology!(frame::Frame, topology::Topology)
__check(lib.chfl_frame_set_topology(__ptr(frame), __const_ptr(topology)))
return nothing
end
"""
Get the `frame` step, *i.e.* the frame number in the trajectory.
"""
function Base.step(frame::Frame)
result = Ref{UInt64}(0)
__check(lib.chfl_frame_step(__const_ptr(frame), result))
return result[]
end
"""
Set the `frame` step to `step`.
"""
function set_step!(frame::Frame, step::Integer)
__check(lib.chfl_frame_set_step(__ptr(frame), UInt64(step)))
return nothing
end
"""
Guess the bonds, angles, and dihedrals in the `frame` using a distance criteria.
"""
function guess_bonds!(frame::Frame)
__check(lib.chfl_frame_guess_bonds(__ptr(frame)))
return nothing
end
"""
Calculate the distance between two atoms.
"""
function distance(frame::Frame, i::Integer, j::Integer)
result = Ref{Float64}(0)
__check(lib.chfl_frame_distance(__const_ptr(frame), UInt64(i), UInt64(j), result))
return result[]
end
"""
Calculate the angle made by three atoms.
"""
function Base.angle(frame::Frame, i::Integer, j::Integer, k::Integer)
result = Ref{Float64}(0)
__check(lib.chfl_frame_angle(__const_ptr(frame), UInt64(i), UInt64(j), UInt64(k), result))
return result[]
end
"""
Calculate the dihedral (torsional) angle made by four unbranched atoms.
"""
function dihedral(frame::Frame, i::Integer, j::Integer, k::Integer, m::Integer)
result = Ref{Float64}(0)
__check(lib.chfl_frame_dihedral(__const_ptr(frame), UInt64(i), UInt64(j), UInt64(k), UInt64(m), result))
return result[]
end
"""
Calculate the out-of-plane (improper) angle made by four atoms.
"""
function out_of_plane(frame::Frame, i::Integer, j::Integer, k::Integer, m::Integer)
result = Ref{Float64}(0)
__check(lib.chfl_frame_out_of_plane(__const_ptr(frame), UInt64(i), UInt64(j), UInt64(k), UInt64(m), result))
return result[]
end
"""
Add an `atom` and the corresponding `position` and `velocity` data to a `frame`.
"""
function add_atom!(frame::Frame, atom::Atom, position::Vector{Float64}, velocity::Vector{Float64}=Float64[0.0,0.0,0.0])
__check(lib.chfl_frame_add_atom(__ptr(frame), __const_ptr(atom), position, velocity))
return nothing
end
"""
Remove the `atom` at `index` from the `frame`.
This function modifies all the `atoms` indexes after `index`, and invalidates
any array obtained using `positions` or `velocities`.
"""
function remove_atom!(frame::Frame, index::Integer)
__check(lib.chfl_frame_remove(__ptr(frame), UInt64(index)))
return nothing
end
"""
Set a named property for the given `Frame`.
"""
function set_property!(frame::Frame, name::String, value)
property = Property(value)
__check(lib.chfl_frame_set_property(
__ptr(frame), pointer(name), __const_ptr(property)
))
return nothing
end
"""
Get a named property for the given atom.
"""
function property(frame::Frame, name::String)::PropertyValue
ptr = lib.chfl_frame_get_property(__const_ptr(frame), pointer(name))
return extract(Property(CxxPointer(ptr, is_const=false)))
end
"""
Get the number of properties associated with a frame.
"""
function properties_count(frame::Frame)
count = Ref{UInt64}(0)
__check(lib.chfl_frame_properties_count(__const_ptr(frame), count))
return Int(count[])
end
"""
Get the names of all properties associated with a frame.
"""
function list_properties(frame::Frame)
count = UInt64(properties_count(frame))
names = Array{Ptr{UInt8}}(undef, count)
__check(lib.chfl_frame_list_properties(__const_ptr(frame), pointer(names), count))
return map(unsafe_string, names)
end
"""
Add an additional bond to the `Frame`'s `Topology`.
"""
function add_bond!(frame::Frame, i::Integer, j::Integer, order=nothing)
if order === nothing
__check(lib.chfl_frame_add_bond(__ptr(frame), UInt64(i), UInt64(j)))
else
# Check that the order is a valid BondOrder
order = BondOrder(Integer(order))
__check(lib.chfl_frame_bond_with_order(
__ptr(frame), UInt64(i), UInt64(j), lib.chfl_bond_order(order)
))
end
return nothing
end
"""
Remove a bond from the `Frame`'s `Topology`.
"""
function remove_bond!(frame::Frame, i::Integer, j::Integer)
__check(lib.chfl_frame_remove_bond(__ptr(frame), UInt64(i), UInt64(j)))
return nothing
end
"""
Remove all bonds, angles and dihedral angles from the `Frame`'s `Topology`.
"""
function clear_bonds!(frame::Frame)
__check(lib.chfl_frame_clear_bonds(__ptr(frame)))
return nothing
end
"""
Add a residue to the `Frame`'s `Topology`.
"""
function add_residue!(frame::Frame, residue::Residue)
__check(lib.chfl_frame_add_residue(__ptr(frame), __const_ptr(residue)))
return nothing
end
"""
Make a deep copy of a `Frame`.
"""
function Base.deepcopy(frame::Frame)
ptr = lib.chfl_frame_copy(__const_ptr(frame))
return Frame(CxxPointer(ptr, is_const=false))
end
# Iteration support
function Base.iterate(frame::Frame, atom=0)
if atom >= size(frame)
return nothing
else
return (Atom(frame, atom), atom + 1)
end
end
Base.eltype(::Frame) = Atom