2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019, 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.
38 * Implements methods from coordinatefile.h
40 * \author Paul Bauer <paul.bauer.q@gmail.com>
41 * \ingroup module_coordinateio
47 #include "coordinatefile.h"
51 #include "gromacs/coordinateio/outputadapters.h"
52 #include "gromacs/coordinateio/requirements.h"
53 #include "gromacs/math/vec.h"
54 #include "gromacs/trajectory/trajectoryframe.h"
55 #include "gromacs/utility/exceptions.h"
61 * Get the internal file type from the \p filename.
63 * \param[in] filename Filename of output file.
64 * \throws InvalidInputError When unable to work on an emoty file name.
65 * \returns integer value of file type.
67 static int getFileType(const std::string
&filename
)
70 if (!filename
.empty())
72 filetype
= fn2ftp(filename
.c_str());
76 GMX_THROW(InvalidInputError("Can not open file with an empty name"));
82 * Get the flag representing the requirements for a given file output.
84 * Also checks if the supplied topology is sufficient through the pointer
87 * \param[in] filetype Internal file type used to check requirements.
88 * \throws InvalidInputError When encountering an invalid file type.
89 * \returns Requirements represent by the bitmask in the return type.
91 static unsigned long getSupportedOutputAdapters(int filetype
)
93 unsigned long supportedOutputAdapters
= 0;
94 supportedOutputAdapters
|= convertFlag(CoordinateFileFlags::Base
);
98 supportedOutputAdapters
|= (convertFlag(CoordinateFileFlags::RequireForceOutput
) |
99 convertFlag(CoordinateFileFlags::RequireVelocityOutput
) |
100 convertFlag(CoordinateFileFlags::RequireAtomConnections
) |
101 convertFlag(CoordinateFileFlags::RequireAtomInformation
) |
102 convertFlag(CoordinateFileFlags::RequireChangedOutputPrecision
));
105 supportedOutputAdapters
|= (convertFlag(CoordinateFileFlags::RequireAtomConnections
) |
106 convertFlag(CoordinateFileFlags::RequireAtomInformation
));
109 supportedOutputAdapters
|= (convertFlag(CoordinateFileFlags::RequireAtomInformation
) |
110 convertFlag(CoordinateFileFlags::RequireVelocityOutput
));
113 supportedOutputAdapters
|= (convertFlag(CoordinateFileFlags::RequireForceOutput
) |
114 convertFlag(CoordinateFileFlags::RequireVelocityOutput
));
117 supportedOutputAdapters
|= (convertFlag(CoordinateFileFlags::RequireChangedOutputPrecision
));
122 GMX_THROW(InvalidInputError("Invalid file type"));
124 return supportedOutputAdapters
;
128 * Creates a new container object with the user requested IOutputAdapter derived
129 * methods attached to it.
131 * \param[in] requirements Specifications for modules to add.
132 * \param[in] atoms Local copy of atom information to use.
133 * \param[in] sel Selection to use for choosing atoms to write out.
134 * \param[in] abilities Specifications for what the output method can do.
135 * \returns New container for IoutputAdapter derived methods.
137 static OutputAdapterContainer
138 addOutputAdapters(const OutputRequirements
&requirements
,
140 const Selection
&sel
,
141 unsigned long abilities
)
143 OutputAdapterContainer
output(abilities
);
145 /* An adapter only gets added if the user has specified a non-default
146 * behaviour. In most cases, this behaviour is passive, meaning that
147 * output gets written if it exists in the input and if the output
150 if (requirements
.velocity
!= ChangeSettingType::PreservedIfPresent
)
153 std::make_unique
<SetVelocities
>(requirements
.velocity
),
154 CoordinateFileFlags::RequireVelocityOutput
);
156 if (requirements
.force
!= ChangeSettingType::PreservedIfPresent
)
159 std::make_unique
<SetForces
>(requirements
.force
),
160 CoordinateFileFlags::RequireForceOutput
);
162 if (requirements
.precision
!= ChangeFrameInfoType::PreservedIfPresent
)
165 std::make_unique
<SetPrecision
>(requirements
.prec
),
166 CoordinateFileFlags::RequireChangedOutputPrecision
);
168 if (requirements
.atoms
!= ChangeAtomsType::PreservedIfPresent
)
171 std::make_unique
<SetAtoms
>(requirements
.atoms
,
173 CoordinateFileFlags::RequireAtomInformation
);
175 if (requirements
.frameTime
!= ChangeFrameTimeType::PreservedIfPresent
)
177 switch (requirements
.frameTime
)
179 case (ChangeFrameTimeType::StartTime
):
180 output
.addAdapter(std::make_unique
<SetStartTime
>(requirements
.startTimeValue
),
181 CoordinateFileFlags::RequireNewFrameStartTime
);
183 case (ChangeFrameTimeType::TimeStep
):
184 output
.addAdapter(std::make_unique
<SetTimeStep
>(requirements
.timeStepValue
),
185 CoordinateFileFlags::RequireNewFrameTimeStep
);
187 case (ChangeFrameTimeType::Both
):
188 output
.addAdapter(std::make_unique
<SetStartTime
>(requirements
.startTimeValue
),
189 CoordinateFileFlags::RequireNewFrameStartTime
);
190 output
.addAdapter(std::make_unique
<SetTimeStep
>(requirements
.timeStepValue
),
191 CoordinateFileFlags::RequireNewFrameTimeStep
);
197 if (requirements
.box
!= ChangeFrameInfoType::PreservedIfPresent
)
200 std::make_unique
<SetBox
>(requirements
.newBox
),
201 CoordinateFileFlags::RequireNewBox
);
206 std::make_unique
<OutputSelector
>(sel
),
207 CoordinateFileFlags::RequireCoordinateSelection
);
212 std::unique_ptr
<TrajectoryFrameWriter
>
213 createTrajectoryFrameWriter(const gmx_mtop_t
*top
,
214 const Selection
&sel
,
215 const std::string
&filename
,
217 OutputRequirements requirements
)
220 * Currently the requirements object is expected to be processed and valid,
221 * meaning that e.g. a new box is specified if requested by the option,
222 * or that time values have been set if the corresponding values are set.
223 * This will need to get revisited when the code that builds this object from
224 * the user options gets merged.
226 int filetype
= getFileType(filename
);
227 unsigned long abilities
= getSupportedOutputAdapters(filetype
);
229 // first, check if we have a special output format that needs atoms
230 if ((filetype
== efPDB
) || (filetype
== efGRO
))
232 if (requirements
.atoms
== ChangeAtomsType::Never
)
234 GMX_THROW(InconsistentInputError("Can not write to PDB or GRO when"
235 "explicitly turning atom information off"));
237 if (requirements
.atoms
!= ChangeAtomsType::AlwaysFromStructure
)
239 requirements
.atoms
= ChangeAtomsType::Always
;
242 OutputAdapterContainer outputAdapters
= addOutputAdapters(requirements
, std::move(atoms
),
245 TrajectoryFrameWriterPointer
trajectoryFrameWriter(
246 new TrajectoryFrameWriter(filename
,
250 std::move(outputAdapters
)));
251 return trajectoryFrameWriter
;
256 * Create a deep copy of a t_trxframe \p input into \p copy
258 * When running the analysis tools and changing values with the
259 * outputadapters, a deep copy of the \p input coordinate frame has to be
260 * created first to ensure that the data is not changed if it is needed for other
261 * tools following with analysis later. Therefore, the data is passed
262 * to \p copy by performing a deep copy first.
264 * The method allocates new storage for coordinates of the x, v, and f arrays
265 * in the new coordinate frame. This means that those arrays need to be free'd
266 * after the frame has been processed and been written to disk.
268 * \param[in] input Reference input coordinate frame.
269 * \param[in,out] copy Pointer to new output frame that will receive the deep copy.
270 * \param[in] xvec Pointer to local coordinate storage vector.
271 * \param[in] vvec Pointer to local velocity storage vector.
272 * \param[in] fvec Pointer to local force storage vector.
273 * \param[in] indexvec Pointer to local index storage vector.
275 static void deepCopy_t_trxframe(const t_trxframe
&input
,
282 copy
->not_ok
= input
.not_ok
;
283 copy
->bStep
= input
.bStep
;
284 copy
->bTime
= input
.bTime
;
285 copy
->bLambda
= input
.bLambda
;
286 copy
->bFepState
= input
.bFepState
;
287 copy
->bAtoms
= input
.bAtoms
;
288 copy
->bPrec
= input
.bPrec
;
292 copy
->bBox
= input
.bBox
;
293 copy
->bDouble
= input
.bDouble
;
294 copy
->natoms
= input
.natoms
;
295 copy
->step
= input
.step
;
296 copy
->time
= input
.time
;
297 copy
->lambda
= input
.lambda
;
298 copy
->fep_state
= input
.fep_state
;
301 copy
->atoms
= input
.atoms
;
303 copy
->prec
= input
.prec
;
306 copy
->x
= as_rvec_array(xvec
);
310 copy
->v
= as_rvec_array(vvec
);
314 copy
->f
= as_rvec_array(fvec
);
318 copy
->index
= indexvec
;
322 copy
->index
= nullptr;
324 for (int i
= 0; i
< copy
->natoms
; i
++)
328 copy_rvec(input
.x
[i
], copy
->x
[i
]);
332 copy_rvec(input
.v
[i
], copy
->v
[i
]);
336 copy_rvec(input
.f
[i
], copy
->f
[i
]);
340 copy
->index
[i
] = input
.index
[i
];
343 copy_mat(input
.box
, copy
->box
);
344 copy
->bPBC
= input
.bPBC
;
345 copy
->ePBC
= input
.ePBC
;
349 * Method to open TNG file.
351 * Only need extra method to open this kind of file as it may need access to
352 * a Selection \p sel, if it is valid. Otherwise atom indices will be taken
353 * from the topology \p mtop.
355 * \param[in] name Name of the output file.
356 * \param[in] sel Reference to selection.
357 * \param[in] mtop Pointer to topology, tested before that it is valid.
358 * \todo Those should be methods in a replacement for t_trxstatus instead.
360 static t_trxstatus
*openTNG(const std::string
&name
, const Selection
&sel
, const gmx_mtop_t
*mtop
)
362 const char *filemode
= "w";
365 GMX_ASSERT(sel
.hasOnlyAtoms(), "Can only work with selections consisting out of atoms");
366 return trjtools_gmx_prepare_tng_writing(name
.c_str(),
368 nullptr, //infile_, //how to get the input file here?
377 return trjtools_gmx_prepare_tng_writing(name
.c_str(),
379 nullptr, //infile_, //how to get the input file here?
383 get_atom_index(mtop
),
388 TrajectoryFileOpener::~TrajectoryFileOpener()
390 close_trx(outputFile_
);
393 t_trxstatus
*TrajectoryFileOpener::outputFile()
395 if (outputFile_
== nullptr)
397 const char *filemode
= "w";
401 outputFile_
= openTNG(outputFileName_
,
410 outputFile_
= open_trx(outputFileName_
.c_str(), filemode
);
413 GMX_THROW(InvalidInputError("Invalid file type"));
420 TrajectoryFrameWriter::prepareAndWriteFrame(const int framenumber
, const t_trxframe
&input
)
422 if (!outputAdapters_
.isEmpty())
425 clear_trxframe(&local
, true);
426 localX_
.resize(input
.natoms
);
427 localIndex_
.resize(input
.natoms
);
430 localV_
.resize(input
.natoms
);
434 localF_
.resize(input
.natoms
);
436 deepCopy_t_trxframe(input
, &local
, localX_
.data(), localV_
.data(),
437 localF_
.data(), localIndex_
.data());
438 for (const auto &outputAdapter
: outputAdapters_
.getAdapters())
442 outputAdapter
->processFrame(framenumber
, &local
);
445 write_trxframe(file_
.outputFile(), &local
, nullptr);
449 write_trxframe(file_
.outputFile(), const_cast<t_trxframe
*>(&input
), nullptr);