2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019,2020, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements methods from mrcserializer.h
39 * \author Christian Blau <cblau@gwdg.de>
40 * \ingroup module_fileio
44 #include "mrcserializer.h"
48 #include "gromacs/fileio/mrcdensitymapheader.h"
49 #include "gromacs/utility/inmemoryserializer.h"
57 /*! \brief Machine stamp to indicate endianess of mrc/ccp4 file.
59 * "EMDB Map Distribution Format Description Version 1.01 (c) emdatabank.org 2014"
60 * The first two bytes of a 4 byte unsigned int are used to indicate endianess:
61 * 0x11 0x11 big endian
62 * 0x44 0x44 small endian
63 * Byte-swap data if appropriate, when transferring data files between machines.
65 enum class MachineStamp
: int32_t
67 bigEndian
= 0x11110000, //!< big endian magic number 0x11 0x11 0x00 0x00 = 286,326,784
68 smallEndian
= 0x44440000, //!< small endian magic number 0x44 0x44 0x00 0x00 = 1,145,307,136
71 /*! \brief Serialize a container of int32_t values.
72 * Serializes all containers with value_type int32_t that may looped over in a
73 * range based for loop and have modifiable elements.
75 * \tparam ContainerType type of container to be serialized
76 * \param[in,out] serializer the serializer
77 * \param[in,out] valueContainer the array to be serialized
79 template<typename ContainerType
>
80 std::enable_if_t
<std::is_same_v
<typename
ContainerType::value_type
, int32_t>, void>
81 serialize(ISerializer
* serializer
, ContainerType
* valueContainer
)
83 for (auto& value
: *valueContainer
)
85 serializer
->doInt32(&value
);
89 /*! \brief Serialize a container of float values.
90 * Serializes all containers with value_type float that may looped over in a
91 * range based for loop and have modifiable elements.
93 * \tparam ContainerType type of container to be serialized
94 * \param[in,out] serializer the serializer
95 * \param[in,out] valueContainer the array to be serialized
97 template<typename ContainerType
>
98 std::enable_if_t
<std::is_same_v
<typename
ContainerType::value_type
, float>, void>
99 serialize(ISerializer
* serializer
, ContainerType
* valueContainer
)
101 for (auto& value
: *valueContainer
)
103 serializer
->doFloat(&value
);
107 //! Serialize and convert from FORTRAN 1-based to C 0-based indices when reading and vice versa when writing
108 void serializeIndex(ISerializer
* serializer
, int32_t* index
)
110 int32_t fortranIndex
;
111 if (!serializer
->reading())
113 fortranIndex
= *index
+ 1;
115 serializer
->doInt32(&fortranIndex
);
116 if (serializer
->reading())
118 *index
= fortranIndex
- 1;
123 * Serializes an integer array and add unity when writing, substracting unity when reading.
125 void serializeIndices(ISerializer
* serializer
, std::array
<int32_t, 3>* valueArray
)
127 for (auto& value
: *valueArray
)
129 serializeIndex(serializer
, &value
);
133 /*! \brief Serialize input as int32_t via static casting.
134 * \tparam IntegralType type to be serialized as int32_t
136 template<class IntegralType
>
137 std::enable_if_t
<(std::is_integral_v
<IntegralType
> || std::is_enum_v
<IntegralType
>), void>
138 serializeAsInt32(ISerializer
* serializer
, IntegralType
* value
)
140 int32_t serializedValue
;
141 if (!serializer
->reading())
143 serializedValue
= static_cast<int32_t>(*value
);
145 serializer
->doInt32(&serializedValue
);
146 if (serializer
->reading())
148 *value
= static_cast<IntegralType
>(serializedValue
);
152 //! conversion constant from nm to MRC distance units (Ångström)
153 constexpr float c_nmToMrcUnits
= 10;
155 //! Convert MRC distances to nm
156 float mrcUnitsToNm(float mrcValue
)
158 return mrcValue
/ c_nmToMrcUnits
;
161 //! Convert nm to MRC distances
162 float nmToMrcUnits(float nmValue
)
164 return nmValue
* c_nmToMrcUnits
;
167 //! Serialize and convert between MRC and GROMACS distance units
168 void serializeDistance(ISerializer
* serializer
, float* distance
)
170 float convertedDistance
;
171 if (!serializer
->reading())
173 convertedDistance
= nmToMrcUnits(*distance
);
175 serializer
->doFloat(&convertedDistance
);
176 if (serializer
->reading())
178 *distance
= mrcUnitsToNm(convertedDistance
);
182 //! Serialize the skew data, words 25-37 in an mrc file.
183 void serializeCrystallographicSkewData(ISerializer
* serializer
, MrcDensitySkewData
* skewData
)
185 /* 25 | LSKFLG | signed int | emdb: 0 or 1
186 * flag for skew matrix
188 serializeAsInt32(serializer
, &(skewData
->valid_
));
189 /* 26-34 | SKWMAT | floating pt | emdb: not set
190 * skew matrix-S11, S12, S13, S21, S22, S23, S31, S32, S33
191 * 35-37 | SKWTRN | floating pt | emdb: not set
192 * skew translation-T1, T2, T3
194 for (auto& matrixEntry
: skewData
->matrix_
)
196 serializeDistance(serializer
, &matrixEntry
);
198 for (auto& translationEntry
: skewData
->translation_
)
200 serializeDistance(serializer
, &translationEntry
);
204 /*! \brief Symmetrise and de-serializes the mrc density map header.
206 * Supports reading EMDB density maps in mrc format according to
207 * ftp://ftp.wwpdb.org/pub/emdb/doc/Map-format/current/EMDB_map_format.pdf
208 * \note format has small differences to http://www.ccpem.ac.uk/mrc_format/mrc2014.php
210 void doMrcDensityMapHeader(ISerializer
* serializer
, MrcDensityMapHeader
* mrcFile
)
212 // 1-3 | NC, NR, NS | signed int >0 | emdb: NC=NR=NS
213 // # of columns (fastest changing),rows, sections (slowest changing)
214 serialize(serializer
, &(mrcFile
->numColumnRowSection_
));
216 // 4 | MODE | signed int | 0,1,2,3,4 | emdb: 2
217 serializeAsInt32(serializer
, &(mrcFile
->dataMode_
));
219 // 5-7 | NCSTART, NRSTART, NSSTART | signed int | position of first column, first row, and first section
220 serialize(serializer
, &(mrcFile
->columnRowSectionStart_
));
222 // 8-10 | NX, NY, NZ | signed int >0 | emdb: same as NC, NR, NS | intervals per unit cell repeat along X,Y Z
223 serialize(serializer
, &(mrcFile
->extent_
));
225 // 11-13 | X_LENGTH, Y_LENGTH, Z_LENGTH | floating pt >0 | emdb Map lengths along X,Y,Z in
226 // Ångström Length in Ångström for a single voxel is unit_cell_length/n_voxel
227 serialize(serializer
, &(mrcFile
->cellLength_
));
229 // 14-16 | ALPHA,BETA,GAMMA | floating pt >0, <180 | emdb: 90, 90, 90
230 // Unit Cell angles (degrees)following IUCr space group conventions for crystals
231 serialize(serializer
, &(mrcFile
->cellAngles_
));
233 // 17-19 | MAPC, MAPR, MAPS | signed int | 1 (=X) 2 (=Y) 3 (=Z)| emdb: 1, 2, 3
234 // relationship of X,Y,Z axes to columns, rows, sections
235 // GROMACS uses C-style X=0, whereas mrc uses FORTRAN-style X=1
236 serializeIndices(serializer
, &(mrcFile
->columnRowSectionToXyz_
));
238 // 20-22 | AMIN, AMAX, AMEAN | floating pt | Minimum, maximum, average density
239 serializer
->doFloat(&(mrcFile
->dataStatistics_
.min_
));
240 serializer
->doFloat(&(mrcFile
->dataStatistics_
.max_
));
241 serializer
->doFloat(&(mrcFile
->dataStatistics_
.mean_
));
243 // 23 | ISPG | signed int 1-230 | emdb: 1; 0 for image stacks | space group number
244 serializeAsInt32(serializer
, &(mrcFile
->spaceGroup_
));
246 // 24 | NSYMBT | signed int | 80n | emdb: 0
247 // # of bytes in symmetry table, expected to be multiple of 80
248 int32_t numBytesExtendedHeader
;
249 if (!serializer
->reading())
251 numBytesExtendedHeader
= mrcFile
->extendedHeader_
.size();
253 serializer
->doInt32(&numBytesExtendedHeader
);
254 if (serializer
->reading())
256 mrcFile
->extendedHeader_
.resize(numBytesExtendedHeader
);
259 // 25-37 | SKEW DATA | 1 byte flag, 9 byte matrix, 3 byte translation
260 serializeCrystallographicSkewData(serializer
, &(mrcFile
->skewData_
));
262 // 38-52 | EXTRA | 32 bit binary
263 // 15 floats of user-defined metadata
264 // EMDB might use fields 50,51 and 52 for setting the coordinate system origin
265 for (auto& userFloat
: mrcFile
->userDefinedFloat_
)
267 serializer
->doFloat(&userFloat
);
270 // 53 | MAP | ASCII char | emdb: "MAP "
271 // MRC/CCP4 MAP format identifier
272 for (auto& formatIdentifierCharacter
: mrcFile
->formatIdentifier_
)
274 serializer
->doUChar(&formatIdentifierCharacter
);
277 // 54 | MACHST | 32 bit | binary machine stamp written/read as 4 hex byte sequence
278 MachineStamp machineStamp
= GMX_INTEGER_BIG_ENDIAN
? MachineStamp::bigEndian
: MachineStamp::smallEndian
;
279 serializeAsInt32(serializer
, &machineStamp
);
281 // 55 | RMS | floating pt | Density root-mean-square deviation
282 serializer
->doFloat(&(mrcFile
->dataStatistics_
.rms_
));
284 // 56 | NLABL | signed int | 0-10 | Number of used user defined labels
285 serializer
->doInt32(&(mrcFile
->labels_
.numUsedLabels_
));
287 // 57-256 | LABEL_N | ASCII char | emdb : “::::EMDataBank.org::::EMD-1234::::”.
288 // 10 user-defined labels each 80 characters
289 for (auto&& label
: mrcFile
->labels_
.labels_
)
291 for (auto&& labelCharacter
: label
)
293 serializer
->doUChar(&labelCharacter
);
297 // 257-257+NSYMBT | user defined extended header information | emdb : none
298 for (auto& extendedHeaderCharacter
: mrcFile
->extendedHeader_
)
300 serializer
->doUChar(&extendedHeaderCharacter
);
306 void serializeMrcDensityMapHeader(ISerializer
* serializer
, const MrcDensityMapHeader
& mrcFile
)
308 MrcDensityMapHeader mrcHeaderCopy
= mrcFile
;
309 doMrcDensityMapHeader(serializer
, &mrcHeaderCopy
);
312 MrcDensityMapHeader
deserializeMrcDensityMapHeader(ISerializer
* serializer
)
314 MrcDensityMapHeader mrcHeader
;
315 doMrcDensityMapHeader(serializer
, &mrcHeader
);