diff --git a/.github/workflows/backend-template.yml b/.github/workflows/backend-template.yml index 72259b407..2bb65b736 100644 --- a/.github/workflows/backend-template.yml +++ b/.github/workflows/backend-template.yml @@ -184,3 +184,12 @@ jobs: name: failed-interface-test-diffs-${{ inputs.backend_name }} path: ${{ inputs.test_interface_directory }}/*/*.diff + - name: Run regression tests for library code with ${{ inputs.backend_name }} (binary restarts) + if: ${{ inputs.test_lib_directory }} + shell: bash + working-directory: ${{ inputs.test_lib_directory }} + env: + COLVARS_BINARY_RESTART: 1 + run: | + apptainer exec ${{github.workspace}}/devel-tools/${{ inputs.container_name }}.sif \ + ./run_tests.sh ${{github.workspace}}/${{ inputs.backend_name }}-source/${{ inputs.rpath_exe }} 0??_* diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index d3e87459c..2c548ea6a 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -142,7 +142,7 @@ if(COLVARS_DEBUG) endif() option(ENABLE_COVERAGE "Enable code coverage reporting" ON) -if(ENABLE_COVERAGE AND (NOT CMAKE_CXX_COMPILER_ID STREQUAL "SunPro")) +if(ENABLE_COVERAGE AND (NOT CMAKE_CXX_COMPILER_ID STREQUAL "SunPro") AND (NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")) target_compile_options(colvars PUBLIC --coverage) target_link_options(colvars PUBLIC --coverage) endif() diff --git a/colvartools/poisson_integrator.cpp b/colvartools/poisson_integrator.cpp index 5e561e29e..01aa220f7 100644 --- a/colvartools/poisson_integrator.cpp +++ b/colvartools/poisson_integrator.cpp @@ -1,3 +1,4 @@ +#include #include #include "colvargrid.h" diff --git a/doc/colvars-refman-main.tex b/doc/colvars-refman-main.tex index 1c5285eb8..c0545db2d 100644 --- a/doc/colvars-refman-main.tex +++ b/doc/colvars-refman-main.tex @@ -171,7 +171,7 @@ Alternately, use the Automatic colvars features in the Actions tab. -Now, clicking ``Edit'' in the Dashboard window (or right-clikcing on a colvar in the list view), you can modify the collective variable to reflect interesting geometric properties of the system. +Now, clicking ``Edit'' in the Dashboard window (or right-clicking on a colvar in the list view), you can modify the collective variable to reflect interesting geometric properties of the system. The power of the collective variables approach lies in the variety of geometric functions (``components'') and their combinations. The editor window provides a number of helpers to make it easy and quick to define the most relevant variables. See section \ref{sec:dashboard_config_editor} for details. @@ -381,7 +381,7 @@ The ``internal units'' of the Colvars module are the units in which values are expected to be in the configuration file, and in which collective variable values, energies, etc. are expressed in the output and colvars trajectory files. Generally \textbf{the Colvars module uses internally the same units as its back-end MD engine, with the exception of VMD}, where different unit sets are supported to allow for easy setup, visualization and analysis of Colvars simulations performed with any simulation engine. -Note that \textbf{angles} are expressed in degrees, and derived quantites such as force constants are based on degrees as well. +Note that \textbf{angles} are expressed in degrees, and derived quantities such as force constants are based on degrees as well. Some colvar components have default values, expressed in \AA{}ngstr\"om in this documentation. They are converted to the current length unit as needed. Atomic coordinates read from \textbf{XYZ files} (and PDB files where applicable) are expected to be expressed in \AA{}ngstr\"om, no matter what unit system is in use by the back-end (\MDENGINE) or the Colvars Module. They are converted internally to the current length unit as needed. Note that force constants in \texttt{harmonic} and \texttt{harmonicWalls} biases (\ref{sec:colvarbias_harmonic}) are rescaled according to the \refkey{width}{colvar|width} parameter of colvars, so that they are formally in energy units, although if \refkey{width}{colvar|width} is given its default value of 1.0, force constants are effectively expressed in \energyunit/\textit{(colvar unit)}$^2$. @@ -481,7 +481,9 @@ string}{% ``out''}{% If a value is provided, it is interpreted as the prefix to all output files that will be written by the Colvars module (see \ref{sec:colvars_output}). - A value must be defined and is set by default, even when Colvars embeds its state information in a LAMMPS restart files, to allow the output of other files (see \ref{sec:colvars_output}). + This includes output state files for checkpointing, as well as accumulated information such as PMFs or histograms and trajectories of the collective variables. + Because this information is often useful, a default value is set for this keyword when not provided. + However, supplying an empty string suppresses any file output from Colvars to file, except for data saved into the LAMMPS binary restart file. } \item % @@ -522,11 +524,11 @@ Thermostating fix (optional)}{% string}{% NULL}{% - This keyword provides the \emph{ID} of an applicable thermostating fix command. This will be used to provide the Colvars module with the current thermostat target temperature when using a method that needs this information.} + This keyword provides the \emph{ID} of an applicable thermostatting fix command. This will be used to provide the Colvars module with the current thermostat target temperature when using a method that needs this information.} \end{itemize} -All of the above keywords except for the name of the configuration file may also be given (or overriden) using \texttt{fix\_modify}, as well as new Colvars configuration files (see \ref{sec:cv_scripting} for more details). +All of the above keywords except for the name of the configuration file may also be given (or overridden) using \texttt{fix\_modify}, as well as new Colvars configuration files (see \ref{sec:cv_scripting} for more details). } @@ -694,7 +696,7 @@ \end{mdexampleinput} The step number contained by the loaded file will be used internally by Colvars to control time-dependent biases, unless \texttt{firstTimestep} is issued, in which case that value will be used.} -\cvnamdonly{When the system's topology is changed during simulation via the \texttt{structure} command (e.g.{} in constant-pH simulations), it is generally best to reset and re-initalize the module from scratch before loading the corresponding snapshot: +\cvnamdonly{When the system's topology is changed during simulation via the \texttt{structure} command (e.g.{} in constant-pH simulations), it is generally best to reset and re-initialize the module from scratch before loading the corresponding snapshot: \begin{mdexampleinput} \-structure~newsystem.psf\\ \-reinitatoms~$<$snapshot$>$\\ @@ -809,7 +811,7 @@ \cvvmdonly{Note that in VMD, any forces applied via \texttt{addforce} do not have any effect on the movement of atoms: this feature is only available for compatibility.} For certain types of variable, the force applied directly on a colvar may be combined with those acting \emph{indirectly} on it via the interatomic force field, making up the \emph{total force}. -When the \refkey{outputTotalForce}{colvar|outputTotalForce} kewyord is enabled, or when a biasing method that makes explicit use of the total force is enabled, the total force may be obtained as: +When the \refkey{outputTotalForce}{colvar|outputTotalForce} keyword is enabled, or when a biasing method that makes explicit use of the total force is enabled, the total force may be obtained as: \cvscriptexampleinputcolvar{gettotalforce}{}{"xi"} \noindent{}Note that not all types of variable support total-force computation, and the value of the total force may not be available immediately within the same simulation step: see the documentation of \refkey{outputTotalForce}{colvar|outputTotalForce} for more details. @@ -956,36 +958,71 @@ \cvsubsec{Input state file}{sec:colvars_input} -A \emph{state file} contains the information produced during a Colvars-based simulation besides atomic data, which is provided by \MDENGINE{}. +Several of the sampling methods implemented in Colvars are time- or history-dependent, i.e.\ they work by accumulating data as a simulation progresses, and use these data to determine their biasing forces. If the simulation engine uses a checkpoint or restart file (as GROMACS and LAMMPS do), any data needed by Colvars are embedded inti that file. Otherwise, a dedicated \emph{state file} can be loaded into Colvars directly. -Because many of the methods implemented in Colvars are history-dependent, this file is often needed to continue a long simulation over consecutive runs. -Such state file is written automatically at the end of any simulation with Colvars, and contains data accumulated during that simulation along with the step number at the end of it. -The step number read from the state file is then used to control such time-dependent biases: because of this essential role, the step number internal to Colvars may not always match the step number reported by the MD program that carried during the simulation (which may instead restart from zero each time). -\cvnamdonly{If a state file is not given, the NAMD command \texttt{firstTimestep} may be used to control the Colvars step number.} +When a dedicated Colvars state file is used, it may be in either one of two formats: +\begin{itemize} + +\item Formatted, i.e.\ \emph{``text''} format, which takes more space and is slower to to load/save but is also portable across different platforms and even different simulation engines (save for changes in physical units). This format is used by default, unless explicitly requested otherwise. + +\item Unformatted, i.e.\ \emph{``binary''} format, which is both space-efficient and quick to load/save, but requires that the same \MDENGINE{} build was used to write the file and that the Colvars configuration remains the same. This format is supported by Colvars versions starting 2023-09-25. +Colvars state files are written in binary format by setting the environment variable ``\texttt{COLVARS\_BINARY\_RESTART}'' to 1. + +\end{itemize} + +\cvsubsubsec{Contents of the state file.}{} +In either format, the state file contains accumulated data as well as the step number at the end of the run. +The step number read from a state file overrides any value that \MDENGINE{} provides, and will be incremented if the simulation proceeds. +This means that the step number used internally by Colvars may not always match the step number reported by \MDENGINE{}. +\cvnamdonly{This is particularly inmportant in NAMD, which represents step numbers as a 32-bit integers that overflows after $\sim$ 2 billion steps, effectively negating the usefulness of the \texttt{firstTimeStep} keyword. However, step numbers are implemented correctly in the Colvars state file.} + +% \cvgromacsonly{\cvsubsubsec{Restarting in GROMACS.}{} TODO } + +\cvlammpsonly{\cvsubsubsec{Restarting in LAMMPS.}{} +For continuing a Colvars-based simulation, the recommended method is using the standard LAMMPS \texttt{read\_restart} command, which reads the Colvars state data from the LAMMPS restart file (in binary format). +\begin{mdexampleinput}{} +\-read\_restart~\emph{filename} +\end{mdexampleinput} +Alternatively, restarting from a Colvars-specific state file is also possible by providing the \texttt{input} keyword to the \texttt{fix colvars} command: +\begin{mdexampleinput}{} +\-fix~Colvars~all~colvars~\emph{configfile}~input~\emph{input\_prefix} +\end{mdexampleinput} +When the ``\texttt{input}'' keyword is used, the contents of the file \texttt{$<$\emph{input\_prefix}$>$.colvar.state} override the information read from the LAMMPS restart file. Finally, a state file may also be loaded after initialization through the ``\texttt{fix\_modify}'' command: +\begin{mdexampleinput}{% +}fix\_modify~Colvars~\texttt{input}~\emph{new\_input\_prefix} +\end{mdexampleinput} +} % end \cvlammpsonly -Depending on the configuration, a state file may need to be loaded issued at the beginning of a new simulation when time-dependent biasing methods are applied (moving restraints, metadynamics, ABF, ...). -\cvnamdonly{When the Colvars module is initialized in NAMD, the \texttt{colvarsInput} keyword can be used to give the name of the state file}. -\cvlammpsonly{When the Colvars fix is defined in LAMMPS, the keyword \texttt{input} can be used to load the state file, although it is typically easier to use the LAMMPS \texttt{read\_restart} to re-initialize Colvars together with other fixes.} -After initialization of the Colvars module, a state file may be loaded at any time \cvnamdonly{with the Tcl command \texttt{cv load}}% -\cvvmdonly{with the Tcl command \texttt{cv load}}% -\cvlammpsonly{with the \texttt{fix\_modify load } LAMMPS command}. -It is possible to load a state file even if the configuration has changed: -for example, new variables may be defined or restraints be added in between consecutive runs. +\cvnamdonly{\cvsubsubsec{Restarting in NAMD.}{} +Before the Colvars module is initialized in NAMD, the \texttt{colvarsInput} keyword can be used to give the name of a state file. +After initialization of the Colvars module, a state file may be loaded at any time with the Tcl command \texttt{cv load}. +Both versions support loading Colvars state files in either format (binary or text). +} % end \cvnamdonly + + +\cvsubsubsec{Restarting after a change in Colvars configuration.}{} +It useful in some cases to modify the configuration of variables or biases between consecutive runs: a typical example would the addition or removal of a restraint in a simulation. +By restarting using text-format Colvars state files, it is possible to read previous data while allowing for changes in configuration. For each newly defined variable or bias, no information will be read from the state file if this is unavailable: such new objects will remain uninitialized until the first compute step. Conversely, any information that the state file has about variables or biases that are no longer defined is silently ignored. -\emph{Because these checks are performed based on the names of variables and biases, it is the user's responsibility to ensure that these definitions are consistent between runs.} +\emph{Because these checks are performed based solely on the names of variables and biases, it is your responsibility to ensure that these names correspond to consistent definitions between runs.} +When restarting using binary state files, configuration changes are not allowed. The easiest solution would be to produce a text-format state file specifically for that purpose. +\ifdefined\cvscriptapi{% +Alternatively, after restarting Colvars using a state file consistent with the previous configuration, the configuration may be changed using the scripting interface (see \ref{sec:cv_scripting}). +}\fi \cvsubsec{Output files}{sec:colvars_output} -During a simulation with collective variables defined, the following three output files are written: +If the output prefix \outputName{} is defined, the following output files are written during a simulation run: \begin{itemize} -\item A \emph{state file}, named \outputName\texttt{.colvars.state}; this file is in ASCII (plain text) format\cvnamdonly{, regardless of the value of \texttt{binaryOutput} in the NAMD configuration}. This file is written at the end of the specified run\cvscriptonly{, but can also be written at any time with the command \texttt{cv save} (\ref{sec:cv_command_loadsave})}.\\ - \emph{This is the only Colvars output file needed to continue a simulation.} +\item A \emph{state file}, named \outputName\texttt{.colvars.state}, which is written at the end of the specified run\cvscriptonly{, and can also be written at any time with the scripting command \texttt{save} (\ref{sec:cv_command_loadsave})}. +This file is in plain text format by default\cvnamdonly{, regardless of the value of \texttt{binaryOutput} of the NAMD coordinate and velocity files}, or in binary format if the environment variable \texttt{COLVARS\_BINARY\_RESTART} is set to a non-zero integer. +The state file is used to continue a simulation, and is required to restart a simulation unless the engine supports embedding information into their checkpoint file (as GROMACS or LAMMPS currently do). \item If the parameter \refkey{colvarsRestartFrequency}{Colvars-global|colvarsRestartFrequency} is larger than zero, a \emph{restart file} is written every that many steps: this file is fully equivalent to the final state file. The name of this file is \restartName\texttt{.colvars.state}. @@ -1096,7 +1133,7 @@ \cvsubsubsec{Grid files: multicolumn text format}{sec:colvar_multicolumn_grid} -Many simulation methods and analysis tools write files that contain functions of the collective variables tabulated on a grid (e.g.{} potentials of mean force or multidimentional histograms) for the purpose of analyzing results. +Many simulation methods and analysis tools write files that contain functions of the collective variables tabulated on a grid (e.g.{} potentials of mean force or multidimensional histograms) for the purpose of analyzing results. Such files are produced by ABF (\ref{sec:colvarbias_abf}), metadynamics (\ref{sec:colvarbias_meta}), multidimensional histograms (\ref{sec:colvarbias_histogram}), as well as any restraint with optional thermodynamic integration support (\ref{sec:colvarbias_ti}). In some cases, these files may also be read as input of a new simulation. @@ -1128,7 +1165,7 @@ \item $\mathtt{periodic}(\xi_{i})$ is set to 1 if and only if $\xi_{i}$ is periodic and the grids' boundaries cover its period. \end{itemize} -How the grid's boundaries affect the sequence of points depends on how the contents of the file were computed. In many cases, such as histograms and PMFs computed by metadynamics (\ref{sec:colvarbias_ebmeta}), the values of $\xi_i$ in the first few columns correspond to the \emph{midpoints} of the corresponing bins, i.e.\ $\xi^{1}_{1} = \mathtt{min}(\xi_{i}) + \mathtt{width}(\xi_{i})/2$. +How the grid's boundaries affect the sequence of points depends on how the contents of the file were computed. In many cases, such as histograms and PMFs computed by metadynamics (\ref{sec:colvarbias_ebmeta}), the values of $\xi_i$ in the first few columns correspond to the \emph{midpoints} of the corresponding bins, i.e.\ $\xi^{1}_{1} = \mathtt{min}(\xi_{i}) + \mathtt{width}(\xi_{i})/2$. However, there is a slightly different format in PMF files computed by ABF (\ref{sec:colvarbias_abf}) or other biases that use thermodynamic integration (\ref{sec:colvarbias_ti}). In these cases, it is free-energy gradients that are accumulated on an \texttt{(npoints)}-long grid along each variable $\xi$: after these gradients are integrated, the resulting PMF is discretized on a slightly larger grid with \texttt{(npoints+1)} points along $\xi$ (unless the interval is periodic). Therefore, the grid's outer edges extend by $\mathtt{width}(\xi_{i})/2$ above and below the specified boundaries, so that for instance $\mathtt{min}(\xi_{i})$ in the header appears to be shifted back by $\mathtt{width}(\xi_{i})/2$ compared to what would be expected. \emph{Please keep this difference in mind when comparing PMFs computed by different methods.} @@ -1796,7 +1833,7 @@ \texttt{coordNum}}{% Pairlist control}{% decimal}{% - 0.0}{This controls the pairlist feature, dictating the minimum value for each summation element in Eq.~\ref{eq:cvc_coordNum} such that the pair that contributed the summation element is included in subsequent simulation timesteps until the next pairlist recalculation. For most applications, this value should be small (eg. 0.001) to avoid missing important contributions to the overall sum. Higher values will improve performance by reducing the number of pairs that contribute to the sum. Values above 1 will exclude all possible pair interactions. Similarly, values below 0 will never exclude a pair from consideration. To ensure continuous forces, Eq.~\ref{eq:cvc_coordNum} is further modified by subtracting the tolerance and then rescaling so that each pair covers the range $\left[0, 1\right]$. + 0.0}{This controls the pair list feature, dictating the minimum value for each summation element in Eq.~\ref{eq:cvc_coordNum} such that the pair that contributed the summation element is included in subsequent simulation timesteps until the next pai r list recalculation. For most applications, this value should be small (eg. 0.001) to avoid missing important contributions to the overall sum. Higher values will improve performance by reducing the number of pairs that contribute to the sum. Values above 1 will exclude all possible pair interactions. Similarly, values below 0 will never exclude a pair from consideration. To ensure continuous forces, Eq.~\ref{eq:cvc_coordNum} is further modified by subtracting the tolerance and then rescaling so that each pair covers the range $\left[0, 1\right]$. } \item % @@ -1806,7 +1843,7 @@ \texttt{coordNum}}{% Pairlist regeneration frequency}{% positive integer}{% - 100}{This controls the pairlist feature, dictating how many steps are taken between regenerating pairlists if the tolerance is greater than 0. + 100}{This controls the pairlist feature, dictating how many steps are taken between regenerating pair lists if the tolerance is greater than 0. } \end{cvcoptions} @@ -1816,7 +1853,7 @@ are less than the cutoff), or $N_{\mathtt{group1}}$ if \texttt{group2CenterOnly} is used. For performance reasons, at least one of \texttt{group1} and \texttt{group2} should be of limited size or \texttt{group2CenterOnly} should be used: the cost of the loop over all pairs grows as $N_{\mathtt{group1}} \times N_{\mathtt{group2}}$. -Setting $\mathtt{tolerance} > 0$ ameliorates this to some degree, although every pair is still checked to regenerate the pairlist. +Setting $\mathtt{tolerance} > 0$ ameliorates this to some degree, although every pair is still checked to regenerate the pair list. @@ -1875,7 +1912,7 @@ 3.3~\AA{}), \texttt{expNumer} (with a default value of 6) and \texttt{expDenom} (with a default value of 8). Unlike \texttt{coordNum}, it requires two atom numbers, \texttt{acceptor} and -\texttt{donor}, to be defined. It returns an adimensional number, +\texttt{donor}, to be defined. It returns a dimensionless number, with values between 0 (acceptor and donor far outside the cutoff distance) and 1 (acceptor and donor much closer than the cutoff). @@ -2287,7 +2324,7 @@ Altogether, these are sufficient to represent all three degrees of freedom of a full rotation. However, they also suffer from the potential ``gimbal lock'' problem, which emerges whenever $\theta \simeq \pm 90^\circ$, which includes also the case where the full rotation is small. Under such conditions, the angles $\phi$ and $\psi$ are both ill-defined and cannot be used as collective variables. -For these reasons, it is highly recommmended that Euler angles are used only in simulations where their range of applicability is \emph{known ahead of time}, and excludes configurations where $\theta \simeq \pm 90^\circ$ altogether. +For these reasons, it is highly recommended that Euler angles are used only in simulations where their range of applicability is \emph{known ahead of time}, and excludes configurations where $\theta \simeq \pm 90^\circ$ altogether. \cvsubsubsec{\texttt{orientation}: orientation from reference coordinates.}{sec:cvc_orientation} \labelkey{colvar|orientation} @@ -3207,7 +3244,7 @@ \texttt{aspathCV} and \texttt{azpathCV}}{% The file name of the path file.}{% UNIX filename}{% - Defines the nodes or images that constitutes the path in CV space. The CVs of an image are listed in a line of \texttt{pathFile} using space-seperated format. Lines from top to button in \texttt{pathFile} corresponds images from initial to last. + Defines the nodes or images that constitutes the path in CV space. The CVs of an image are listed in a line of \texttt{pathFile} using space-separated format. Lines from top to button in \texttt{pathFile} corresponds images from initial to last. } \end{cvcoptions} @@ -4104,7 +4141,7 @@ Histograms (\ref{sec:colvarbias_histogram}), ABF (\ref{sec:colvarbias_abf}) and metadynamics (\ref{sec:colvarbias_meta}) all use this number as the initial choice for the grid spacing along this variable. As a typical rule of thumb, \texttt{width} should be no larger than the standard deviation of the colvar in an unbiased simulation (to characterize a local free-energy minimum with at least two points). - Further, many restraints such as harmonic potentials (\ref{sec:colvarbias_harmonic}), harmonic walls (\ref{sec:colvarbias_harmonic_walls}) and linear restraints (\ref{sec:colvarbias_linear}) also use this parameter to define the \emph{expected fluctuations} of the colvar, alowing to express the force constant in terms of this unit. + Further, many restraints such as harmonic potentials (\ref{sec:colvarbias_harmonic}), harmonic walls (\ref{sec:colvarbias_harmonic_walls}) and linear restraints (\ref{sec:colvarbias_linear}) also use this parameter to define the \emph{expected fluctuations} of the colvar, allowing to express the force constant in terms of this unit. This is most useful with multi-dimensional restraints acting on variables that have very different units (for examples, working with \lengthunit{} and degrees $^\circ$ simultaneously): a single force constant can be used for all, which is converted to the respective unit of each variable when forces are applied (the are printed at initialization time. } @@ -5205,7 +5242,7 @@ free energy gradient at the current point $\bm{\xi}$ in the collective variable subspace, and $\alpha(N_\xi)$ is a scaling factor that is ramped from 0 to 1 as the local number of samples $N_\xi$ increases -to prevent nonequilibrium effects in the early phase of the simulation, +to prevent non-equilibrium effects in the early phase of the simulation, when the gradient estimate has a large variance. See the \texttt{fullSamples} parameter below for details. @@ -5214,7 +5251,7 @@ force introduced in the equations of motion guarantees that in the bin centered around $\bm{\xi}$, the forces acting along the selected collective variables average -to zero over time. Eventually, as the undelying free energy surface is canceled +to zero over time. Eventually, as the underlying free energy surface is canceled by the adaptive bias, evolution of the system along $\bm{\xi}$ is governed mainly by diffusion. Although this implementation of ABF can in principle be used in @@ -5282,7 +5319,7 @@ to application of the ABF} {positive integer} {200} - {To avoid nonequilibrium effects due to large fluctuations of the force exerted along the + {To avoid non-equilibrium effects due to large fluctuations of the force exerted along the colvars, it is recommended to apply a biasing force only after a the estimate has started converging. If \texttt{fullSamples} is non-zero, the applied biasing force is scaled by a factor $\alpha(N_\xi)$ between 0 and 1. @@ -5753,13 +5790,13 @@ V_{\mathrm{meta}}(\bm{\xi},t)dt} } \end{equation} -where $t_{e}$ is the time after which the bias potential grows (approximately) evenly during the simulation and $t_{tot}$ is the total simulation time. The free energy calculated according to eq.~\ref{eq:colvars_meta_fes_av} can thus be obtained averaging on time mutiple time-dependent free energy estimates, that can be printed out through the keyword \texttt{keepFreeEnergyFiles}. An alternative is to obtain the free energy profiles by summing the hills added during the simulation; the hills trajectory can be printed out by enabling the option \texttt{writeHillsTrajectory}. +where $t_{e}$ is the time after which the bias potential grows (approximately) evenly during the simulation and $t_{tot}$ is the total simulation time. The free energy calculated according to eq.~\ref{eq:colvars_meta_fes_av} can thus be obtained averaging on time multiple time-dependent free energy estimates, that can be printed out through the keyword \texttt{keepFreeEnergyFiles}. An alternative is to obtain the free energy profiles by summing the hills added during the simulation; the hills trajectory can be printed out by enabling the option \texttt{writeHillsTrajectory}. \cvsubsubsec{Treatment of the PMF boundaries}{sec:colvarbias_meta_boundaries} In typical scenarios the Gaussian hills of a metadynamics potential are interpolated and summed together onto a grid, which is much more efficient than computing each hill independently at every step (the keyword \refkey{useGrids}{metadynamics|useGrids} is \texttt{on} by default). -This numerical approximation typically yields neglibile errors in the resulting PMF \cite{Fiorin2013}. +This numerical approximation typically yields negligible errors in the resulting PMF \cite{Fiorin2013}. However, due to the finite thickness of the Gaussian function, the metadynamics potential would suddenly vanish each time a variable exceeds its grid boundaries. To avoid such discontinuity the Colvars metadynamics code will keep an explicit copy of each hill that straddles a grid's boundary, and will use it to compute metadynamics forces outside the grid. @@ -6105,7 +6142,7 @@ \\ \end{tabular} -\textbf{Tip:} Besides setting a meaninful value for \texttt{targetDistMinVal}, the exploration of unphysically low values of the target distribution (which would lead to very large hills and possibly numerical instabilities) can be also prevented by restricting sampling to a given interval, using e.g.{} \texttt{harmonicWalls} restraint (\ref{sec:colvarbias_harmonic_walls}). +\textbf{Tip:} Besides setting a meaningful value for \texttt{targetDistMinVal}, the exploration of unphysically low values of the target distribution (which would lead to very large hills and possibly numerical instabilities) can be also prevented by restricting sampling to a given interval, using e.g.{} \texttt{harmonicWalls} restraint (\ref{sec:colvarbias_harmonic_walls}). @@ -6372,7 +6409,7 @@ boolean}{% \texttt{off}}{% If this option is chosen and \texttt{colvarsTrajFrequency} is not zero, the positions of the restraint centers will be written to the trajectory file during the simulation. - This option allows to conveniently extract the PMF from the colvars trajectory files in a steered MD calculation. + This option allows to conveniently extract the PMF from the Colvars trajectory files in a steered MD calculation. } \end{itemize} diff --git a/gromacs/tests/library/run_tests.sh b/gromacs/tests/library/run_tests.sh index 103a09c8c..562273d7c 100755 --- a/gromacs/tests/library/run_tests.sh +++ b/gromacs/tests/library/run_tests.sh @@ -223,6 +223,12 @@ for dir in ${DIRLIST} ; do for f in AutoDiff/* do base=`basename $f` + + if [ "${base%.state.stripped}" != "${base}" ] && [ -n "${COLVARS_BINARY_RESTART}" ] ; then + # Do not try comparing binary state files, they will never match anyway + continue + fi + if [ ! -f $base ] ; then echo -e "\n*** File $(${TPUT_RED})$base$(${TPUT_CLEAR}) is missing. ***" SUCCESS=0 diff --git a/lammps/doc/src/fix_colvars.rst b/lammps/doc/src/fix_colvars.rst index 9ac9893c2..936fdaa83 100644 --- a/lammps/doc/src/fix_colvars.rst +++ b/lammps/doc/src/fix_colvars.rst @@ -85,9 +85,10 @@ selection keywords in the Colvars configuration file or files. There is no need to define multiple "fix colvars" instances and it is not allowed. -The "output" keyword allows to specify the prefix of output files -generated by Colvars, for example "*output*.colvars.traj" or -"output.pmf". +The "output" keyword allows to specify the prefix of output files generated +by Colvars, for example "*output*.colvars.traj" or "output.pmf". Supplying +an empty string suppresses any file output from Colvars to file, except for +data saved into the LAMMPS :doc:`binary restart ` files. The "input" keyword allows to specify an optional state file that contains the restart information needed to continue a previous simulation state. diff --git a/lammps/lib/colvars/Makefile.common b/lammps/lib/colvars/Makefile.common index 9e7be1295..e645d8457 100644 --- a/lammps/lib/colvars/Makefile.common +++ b/lammps/lib/colvars/Makefile.common @@ -59,6 +59,7 @@ COLVARS_SRCS = \ colvarscript_commands.cpp \ colvarscript_commands_bias.cpp \ colvarscript_commands_colvar.cpp \ + colvars_memstream.cpp \ colvartypes.cpp \ colvarvalue.cpp \ colvar_neuralnetworkcompute.cpp diff --git a/lammps/lib/colvars/Makefile.deps b/lammps/lib/colvars/Makefile.deps index 3ed21bba8..dee9cbca6 100644 --- a/lammps/lib/colvars/Makefile.deps +++ b/lammps/lib/colvars/Makefile.deps @@ -1,201 +1,231 @@ -$(COLVARS_OBJ_DIR)colvaratoms.o: colvaratoms.cpp colvarmodule.h \ - colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \ +$(COLVARS_OBJ_DIR)colvaratoms.o: colvaratoms.cpp colvardeps.h \ + colvarmodule.h colvars_version.h colvarparse.h colvarvalue.h \ + colvartypes.h ../../src/math_eigen_impl.h colvarparams.h colvarproxy.h \ colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvarparse.h colvarparams.h colvaratoms.h \ - colvardeps.h colvar_rotation_derivative.h + colvarproxy_volmaps.h colvaratoms.h colvar_rotation_derivative.h $(COLVARS_OBJ_DIR)colvarbias_abf.o: colvarbias_abf.cpp colvarmodule.h \ - colvars_version.h colvar.h colvarvalue.h colvartypes.h colvarparse.h \ - colvarparams.h colvardeps.h colvarbias_abf.h colvarproxy.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvarbias.h colvargrid.h colvar_UIestimator.h + colvars_version.h colvar.h colvarvalue.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvardeps.h \ + colvarbias_abf.h colvarproxy.h colvarproxy_io.h colvarproxy_system.h \ + colvarproxy_tcl.h colvarproxy_volmaps.h colvarbias.h colvargrid.h \ + colvar_UIestimator.h $(COLVARS_OBJ_DIR)colvarbias_alb.o: colvarbias_alb.cpp colvarmodule.h \ - colvars_version.h colvarbias.h colvar.h colvarvalue.h colvartypes.h \ - colvarparse.h colvarparams.h colvardeps.h colvarbias_alb.h + colvars_version.h colvarproxy.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvarbias.h colvar.h colvarparse.h colvarparams.h colvardeps.h \ + colvarbias_alb.h $(COLVARS_OBJ_DIR)colvarbias.o: colvarbias.cpp colvarmodule.h \ - colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvarbias.h colvar.h colvarparse.h colvarparams.h \ - colvardeps.h colvargrid.h + colvars_version.h colvarproxy.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvarbias.h colvar.h colvarparse.h colvarparams.h colvardeps.h \ + colvargrid.h $(COLVARS_OBJ_DIR)colvarbias_histogram.o: colvarbias_histogram.cpp \ colvarmodule.h colvars_version.h colvarproxy.h colvartypes.h \ - colvarvalue.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvar.h colvarparse.h colvarparams.h colvardeps.h \ - colvarbias_histogram.h colvarbias.h colvargrid.h + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h colvar.h \ + colvarparse.h colvarparams.h colvardeps.h colvarbias_histogram.h \ + colvarbias.h colvargrid.h $(COLVARS_OBJ_DIR)colvarbias_histogram_reweight_amd.o: \ colvarbias_histogram_reweight_amd.cpp \ colvarbias_histogram_reweight_amd.h colvarbias_histogram.h colvarbias.h \ colvar.h colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \ - colvarparse.h colvarparams.h colvardeps.h colvargrid.h colvarproxy.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvardeps.h \ + colvargrid.h colvarproxy.h colvarproxy_io.h colvarproxy_system.h \ + colvarproxy_tcl.h colvarproxy_volmaps.h $(COLVARS_OBJ_DIR)colvarbias_meta.o: colvarbias_meta.cpp colvarmodule.h \ - colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvar.h colvarparse.h colvarparams.h colvardeps.h \ - colvarbias_meta.h colvarbias.h colvargrid.h + colvars_version.h colvarproxy.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h colvar.h \ + colvarparse.h colvarparams.h colvardeps.h colvarbias_meta.h colvarbias.h \ + colvargrid.h $(COLVARS_OBJ_DIR)colvarbias_restraint.o: colvarbias_restraint.cpp \ colvarmodule.h colvars_version.h colvarproxy.h colvartypes.h \ - colvarvalue.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvarbias_restraint.h colvarbias.h colvar.h \ - colvarparse.h colvarparams.h colvardeps.h + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvarbias_restraint.h colvarbias.h colvar.h colvarparse.h \ + colvarparams.h colvardeps.h $(COLVARS_OBJ_DIR)colvarcomp_alchlambda.o: colvarcomp_alchlambda.cpp \ colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \ - colvarparse.h colvarparams.h colvar.h colvardeps.h colvarcomp.h \ - colvaratoms.h colvarproxy.h colvarproxy_io.h colvarproxy_system.h \ - colvarproxy_tcl.h colvarproxy_volmaps.h colvar_arithmeticpath.h \ - colvar_geometricpath.h + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvar.h \ + colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvar_arithmeticpath.h colvar_geometricpath.h $(COLVARS_OBJ_DIR)colvarcomp_angles.o: colvarcomp_angles.cpp \ colvarmodule.h colvars_version.h colvar.h colvarvalue.h colvartypes.h \ - colvarparse.h colvarparams.h colvardeps.h colvarcomp.h colvaratoms.h \ - colvarproxy.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvardeps.h \ + colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvar_arithmeticpath.h colvar_geometricpath.h $(COLVARS_OBJ_DIR)colvarcomp_apath.o: colvarcomp_apath.cpp colvarmodule.h \ - colvars_version.h colvarvalue.h colvartypes.h colvarparse.h \ - colvarparams.h colvar.h colvardeps.h colvarcomp.h colvaratoms.h \ - colvarproxy.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h + colvars_version.h colvarvalue.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvar.h \ + colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvar_arithmeticpath.h colvar_geometricpath.h $(COLVARS_OBJ_DIR)colvarcomp_coordnums.o: colvarcomp_coordnums.cpp \ colvarmodule.h colvars_version.h colvarparse.h colvarvalue.h \ - colvartypes.h colvarparams.h colvaratoms.h colvarproxy.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ + colvartypes.h ../../src/math_eigen_impl.h colvarparams.h colvaratoms.h \ + colvarproxy.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ colvarproxy_volmaps.h colvardeps.h colvar.h colvarcomp.h \ colvar_arithmeticpath.h colvar_geometricpath.h $(COLVARS_OBJ_DIR)colvarcomp.o: colvarcomp.cpp colvarmodule.h \ - colvars_version.h colvarvalue.h colvartypes.h colvar.h colvarparse.h \ - colvarparams.h colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h + colvars_version.h colvarvalue.h colvartypes.h \ + ../../src/math_eigen_impl.h colvar.h colvarparse.h colvarparams.h \ + colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvar_arithmeticpath.h colvar_geometricpath.h $(COLVARS_OBJ_DIR)colvarcomp_distances.o: colvarcomp_distances.cpp \ colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \ - colvarparse.h colvarparams.h colvar.h colvardeps.h colvarcomp.h \ - colvaratoms.h colvarproxy.h colvarproxy_io.h colvarproxy_system.h \ - colvarproxy_tcl.h colvarproxy_volmaps.h colvar_arithmeticpath.h \ - colvar_geometricpath.h colvar_rotation_derivative.h -$(COLVARS_OBJ_DIR)colvarcomp_gpath.o: colvarcomp_gpath.cpp colvarmodule.h \ - colvars_version.h colvarvalue.h colvartypes.h colvarparse.h \ - colvarparams.h colvar.h colvardeps.h colvarcomp.h colvaratoms.h \ - colvarproxy.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h -$(COLVARS_OBJ_DIR)colvarcomp_neuralnetwork.o: \ - colvarcomp_neuralnetwork.cpp colvarmodule.h colvars_version.h \ - colvarvalue.h colvartypes.h colvarparse.h colvarparams.h colvar.h \ + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvar.h \ colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ colvar_arithmeticpath.h colvar_geometricpath.h \ + colvar_rotation_derivative.h +$(COLVARS_OBJ_DIR)colvarcomp_gpath.o: colvarcomp_gpath.cpp colvarmodule.h \ + colvars_version.h colvarvalue.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvar.h \ + colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvar_arithmeticpath.h colvar_geometricpath.h +$(COLVARS_OBJ_DIR)colvarcomp_neuralnetwork.o: \ + colvarcomp_neuralnetwork.cpp colvarmodule.h colvars_version.h \ + colvarvalue.h colvartypes.h ../../src/math_eigen_impl.h colvarparse.h \ + colvarparams.h colvar.h colvardeps.h colvarcomp.h colvaratoms.h \ + colvarproxy.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ + colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h \ colvar_neuralnetworkcompute.h $(COLVARS_OBJ_DIR)colvarcomp_combination.o: colvarcomp_combination.cpp \ - colvarcomp.h colvarmodule.h colvars_version.h colvar.h colvarvalue.h \ - colvartypes.h colvarparse.h colvarparams.h colvardeps.h colvaratoms.h \ - colvarproxy.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h + colvarcomp.h colvarmodule.h colvars_version.h colvaratoms.h \ + colvarproxy.h colvartypes.h ../../src/math_eigen_impl.h colvarvalue.h \ + colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ + colvarproxy_volmaps.h colvarparse.h colvarparams.h colvardeps.h colvar.h \ + colvar_arithmeticpath.h colvar_geometricpath.h $(COLVARS_OBJ_DIR)colvarcomp_protein.o: colvarcomp_protein.cpp \ colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \ - colvarparse.h colvarparams.h colvar.h colvardeps.h colvarcomp.h \ - colvaratoms.h colvarproxy.h colvarproxy_io.h colvarproxy_system.h \ - colvarproxy_tcl.h colvarproxy_volmaps.h colvar_arithmeticpath.h \ - colvar_geometricpath.h + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvar.h \ + colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvar_arithmeticpath.h colvar_geometricpath.h $(COLVARS_OBJ_DIR)colvarcomp_rotations.o: colvarcomp_rotations.cpp \ colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \ - colvarparse.h colvarparams.h colvar.h colvardeps.h colvarcomp.h \ - colvaratoms.h colvarproxy.h colvarproxy_io.h colvarproxy_system.h \ - colvarproxy_tcl.h colvarproxy_volmaps.h colvar_arithmeticpath.h \ - colvar_geometricpath.h colvar_rotation_derivative.h + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvar.h \ + colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvar_arithmeticpath.h colvar_geometricpath.h \ + colvar_rotation_derivative.h $(COLVARS_OBJ_DIR)colvarcomp_volmaps.o: colvarcomp_volmaps.cpp \ colvarmodule.h colvars_version.h colvarvalue.h colvartypes.h \ - colvarparse.h colvarparams.h colvar.h colvardeps.h colvarcomp.h \ - colvaratoms.h colvarproxy.h colvarproxy_io.h colvarproxy_system.h \ - colvarproxy_tcl.h colvarproxy_volmaps.h colvar_arithmeticpath.h \ - colvar_geometricpath.h -$(COLVARS_OBJ_DIR)colvar.o: colvar.cpp colvarmodule.h colvars_version.h \ - colvarvalue.h colvartypes.h colvarparse.h colvarparams.h colvar.h \ + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvar.h \ colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ - colvar_arithmeticpath.h colvar_geometricpath.h colvarscript.h \ - colvarbias.h colvarscript_commands.h colvarscript_commands_colvar.h \ - colvarscript_commands_bias.h + colvar_arithmeticpath.h colvar_geometricpath.h +$(COLVARS_OBJ_DIR)colvar.o: colvar.cpp colvarmodule.h colvars_version.h \ + colvarvalue.h colvartypes.h ../../src/math_eigen_impl.h colvarparse.h \ + colvarparams.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvardeps.h colvar.h colvar_arithmeticpath.h colvar_geometricpath.h \ + colvarscript.h colvarbias.h colvarscript_commands.h \ + colvarscript_commands_colvar.h colvarscript_commands_bias.h \ + colvars_memstream.h $(COLVARS_OBJ_DIR)colvardeps.o: colvardeps.cpp colvarmodule.h \ - colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvardeps.h colvarparse.h colvarparams.h + colvars_version.h colvarproxy.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvardeps.h colvarparse.h colvarparams.h $(COLVARS_OBJ_DIR)colvargrid.o: colvargrid.cpp colvarmodule.h \ - colvars_version.h colvarvalue.h colvartypes.h colvarparse.h \ - colvarparams.h colvar.h colvardeps.h colvarcomp.h colvaratoms.h \ - colvarproxy.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvar_arithmeticpath.h colvar_geometricpath.h \ - colvargrid.h colvargrid_def.h + colvars_version.h colvarvalue.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h colvar.h \ + colvardeps.h colvarcomp.h colvaratoms.h colvarproxy.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvar_arithmeticpath.h colvar_geometricpath.h colvargrid.h \ + colvargrid_def.h $(COLVARS_OBJ_DIR)colvarmodule.o: colvarmodule.cpp colvarmodule.h \ colvars_version.h colvarparse.h colvarvalue.h colvartypes.h \ - colvarparams.h colvarproxy.h colvarproxy_io.h colvarproxy_system.h \ - colvarproxy_tcl.h colvarproxy_volmaps.h colvar.h colvardeps.h \ - colvarbias.h colvarbias_abf.h colvargrid.h colvar_UIestimator.h \ - colvarbias_alb.h colvarbias_histogram.h \ - colvarbias_histogram_reweight_amd.h colvarbias_meta.h \ - colvarbias_restraint.h colvarscript.h colvarscript_commands.h \ - colvarscript_commands_colvar.h colvarscript_commands_bias.h \ - colvaratoms.h colvarcomp.h colvar_arithmeticpath.h \ - colvar_geometricpath.h colvarmodule_refs.h + ../../src/math_eigen_impl.h colvarparams.h colvarproxy.h \ + colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ + colvarproxy_volmaps.h colvar.h colvardeps.h colvarbias.h \ + colvarbias_abf.h colvargrid.h colvar_UIestimator.h colvarbias_alb.h \ + colvarbias_histogram.h colvarbias_histogram_reweight_amd.h \ + colvarbias_meta.h colvarbias_restraint.h colvarscript.h \ + colvarscript_commands.h colvarscript_commands_colvar.h \ + colvarscript_commands_bias.h colvaratoms.h colvarcomp.h \ + colvar_arithmeticpath.h colvar_geometricpath.h colvarmodule_refs.h $(COLVARS_OBJ_DIR)colvarparams.o: colvarparams.cpp colvarmodule.h \ - colvars_version.h colvarvalue.h colvartypes.h colvarparams.h + colvars_version.h colvarvalue.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarparams.h $(COLVARS_OBJ_DIR)colvarparse.o: colvarparse.cpp colvarmodule.h \ - colvars_version.h colvarvalue.h colvartypes.h colvarparse.h \ - colvarparams.h + colvars_version.h colvarvalue.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarparse.h colvarparams.h \ + colvars_memstream.h $(COLVARS_OBJ_DIR)colvarproxy.o: colvarproxy.cpp colvarmodule.h \ - colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvarscript.h colvarbias.h colvar.h colvarparse.h \ - colvarparams.h colvardeps.h colvarscript_commands.h \ - colvarscript_commands_colvar.h colvarscript_commands_bias.h \ - colvaratoms.h colvarmodule_utils.h + colvars_version.h colvarproxy.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvarscript.h colvarbias.h colvar.h colvarparse.h colvarparams.h \ + colvardeps.h colvarscript_commands.h colvarscript_commands_colvar.h \ + colvarscript_commands_bias.h colvaratoms.h colvarmodule_utils.h $(COLVARS_OBJ_DIR)colvarproxy_io.o: colvarproxy_io.cpp colvarmodule.h \ colvars_version.h colvarproxy_io.h $(COLVARS_OBJ_DIR)colvarproxy_replicas.o: colvarproxy_replicas.cpp \ colvarmodule.h colvars_version.h colvarproxy.h colvartypes.h \ - colvarvalue.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h $(COLVARS_OBJ_DIR)colvarproxy_system.o: colvarproxy_system.cpp \ - colvarmodule.h colvars_version.h colvartypes.h colvarproxy_system.h + colvarmodule.h colvars_version.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarproxy_system.h $(COLVARS_OBJ_DIR)colvarproxy_tcl.o: colvarproxy_tcl.cpp colvarmodule.h \ - colvars_version.h colvarproxy.h colvartypes.h colvarvalue.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvaratoms.h colvarparse.h colvarparams.h \ - colvardeps.h + colvars_version.h colvarproxy.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ + colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ + colvaratoms.h colvarparse.h colvarparams.h colvardeps.h $(COLVARS_OBJ_DIR)colvarproxy_volmaps.o: colvarproxy_volmaps.cpp \ colvarmodule.h colvars_version.h colvarproxy_volmaps.h \ colvarmodule_utils.h $(COLVARS_OBJ_DIR)colvarscript.o: colvarscript.cpp colvarproxy.h \ - colvarmodule.h colvars_version.h colvartypes.h colvarvalue.h \ - colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvardeps.h colvarparse.h colvarparams.h \ - colvarscript.h colvarbias.h colvar.h colvarscript_commands.h \ - colvarscript_commands_colvar.h colvarscript_commands_bias.h -$(COLVARS_OBJ_DIR)colvarscript_commands.o: colvarscript_commands.cpp \ - colvarproxy.h colvarmodule.h colvars_version.h colvartypes.h \ - colvarvalue.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ - colvarproxy_volmaps.h colvardeps.h colvarparse.h colvarparams.h \ - colvarscript.h colvarbias.h colvar.h colvarscript_commands.h \ - colvarscript_commands_colvar.h colvarscript_commands_bias.h -$(COLVARS_OBJ_DIR)colvarscript_commands_bias.o: \ - colvarscript_commands_bias.cpp colvarproxy.h colvarmodule.h \ - colvars_version.h colvartypes.h colvarvalue.h colvarproxy_io.h \ + colvarmodule.h colvars_version.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ colvardeps.h colvarparse.h colvarparams.h colvarscript.h colvarbias.h \ colvar.h colvarscript_commands.h colvarscript_commands_colvar.h \ colvarscript_commands_bias.h -$(COLVARS_OBJ_DIR)colvarscript_commands_colvar.o: \ - colvarscript_commands_colvar.cpp colvarproxy.h colvarmodule.h \ - colvars_version.h colvartypes.h colvarvalue.h colvarproxy_io.h \ +$(COLVARS_OBJ_DIR)colvarscript_commands.o: colvarscript_commands.cpp \ + colvarproxy.h colvarmodule.h colvars_version.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvarproxy_io.h \ colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h \ colvardeps.h colvarparse.h colvarparams.h colvarscript.h colvarbias.h \ colvar.h colvarscript_commands.h colvarscript_commands_colvar.h \ colvarscript_commands_bias.h +$(COLVARS_OBJ_DIR)colvarscript_commands_bias.o: \ + colvarscript_commands_bias.cpp colvarproxy.h colvarmodule.h \ + colvars_version.h colvartypes.h ../../src/math_eigen_impl.h \ + colvarvalue.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ + colvarproxy_volmaps.h colvardeps.h colvarparse.h colvarparams.h \ + colvarscript.h colvarbias.h colvar.h colvarscript_commands.h \ + colvarscript_commands_colvar.h colvarscript_commands_bias.h +$(COLVARS_OBJ_DIR)colvarscript_commands_colvar.o: \ + colvarscript_commands_colvar.cpp colvarproxy.h colvarmodule.h \ + colvars_version.h colvartypes.h ../../src/math_eigen_impl.h \ + colvarvalue.h colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ + colvarproxy_volmaps.h colvardeps.h colvarparse.h colvarparams.h \ + colvarscript.h colvarbias.h colvar.h colvarscript_commands.h \ + colvarscript_commands_colvar.h colvarscript_commands_bias.h +$(COLVARS_OBJ_DIR)colvars_memstream.o: colvars_memstream.cpp \ + colvarmodule.h colvars_version.h colvartypes.h \ + ../../src/math_eigen_impl.h colvarvalue.h colvars_memstream.h $(COLVARS_OBJ_DIR)colvartypes.o: colvartypes.cpp colvarmodule.h \ - colvars_version.h colvartypes.h colvarparse.h colvarvalue.h \ - colvarparams.h colvar_rotation_derivative.h ../../src/math_eigen_impl.h + colvars_version.h colvartypes.h ../../src/math_eigen_impl.h \ + colvarparse.h colvarvalue.h colvarparams.h colvaratoms.h colvarproxy.h \ + colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ + colvarproxy_volmaps.h colvardeps.h colvar_rotation_derivative.h $(COLVARS_OBJ_DIR)colvarvalue.o: colvarvalue.cpp colvarmodule.h \ - colvars_version.h colvarvalue.h colvartypes.h + colvars_version.h colvarvalue.h colvartypes.h \ + ../../src/math_eigen_impl.h colvars_memstream.h $(COLVARS_OBJ_DIR)colvar_neuralnetworkcompute.o: \ colvar_neuralnetworkcompute.cpp colvar_neuralnetworkcompute.h \ colvarparse.h colvarmodule.h colvars_version.h colvarvalue.h \ - colvartypes.h colvarparams.h colvarproxy.h colvarproxy_io.h \ - colvarproxy_system.h colvarproxy_tcl.h colvarproxy_volmaps.h + colvartypes.h ../../src/math_eigen_impl.h colvarparams.h colvarproxy.h \ + colvarproxy_io.h colvarproxy_system.h colvarproxy_tcl.h \ + colvarproxy_volmaps.h diff --git a/lammps/src/COLVARS/colvarproxy_lammps.cpp b/lammps/src/COLVARS/colvarproxy_lammps.cpp index 95513547c..c2435cd2b 100644 --- a/lammps/src/COLVARS/colvarproxy_lammps.cpp +++ b/lammps/src/COLVARS/colvarproxy_lammps.cpp @@ -206,23 +206,6 @@ double colvarproxy_lammps::compute() return bias_energy; } -void colvarproxy_lammps::serialize_status(std::string &rst) -{ - std::ostringstream os; - colvars->write_restart(os); - rst = os.str(); -} - -// set status from string -bool colvarproxy_lammps::deserialize_status(std::string &rst) -{ - if (! colvarproxy_io::input_stream_from_string("input state string", rst)) { - return false; - } else { - return true; - } -} - cvm::rvector colvarproxy_lammps::position_distance(cvm::atom_pos const &pos1, cvm::atom_pos const &pos2) diff --git a/lammps/src/COLVARS/colvarproxy_lammps.h b/lammps/src/COLVARS/colvarproxy_lammps.h index df624960a..d55a334a2 100644 --- a/lammps/src/COLVARS/colvarproxy_lammps.h +++ b/lammps/src/COLVARS/colvarproxy_lammps.h @@ -76,12 +76,6 @@ class colvarproxy_lammps : public colvarproxy { // perform colvars computation. returns biasing energy double compute(); - // dump status to string - void serialize_status(std::string &); - - // set status from string - bool deserialize_status(std::string &); - // Request to set the units used internally by Colvars int set_unit_system(std::string const &units_in, bool check_only = false) override; diff --git a/lammps/src/COLVARS/fix_colvars.cpp b/lammps/src/COLVARS/fix_colvars.cpp index bca3a37e1..246e905a5 100644 --- a/lammps/src/COLVARS/fix_colvars.cpp +++ b/lammps/src/COLVARS/fix_colvars.cpp @@ -45,9 +45,11 @@ #include #include -#include "colvarproxy_lammps.h" #include "colvarmodule.h" +#include "colvarproxy.h" +#include "colvarproxy_lammps.h" #include "colvarscript.h" +#include "colvars_memstream.h" /* struct for packed data communication of coordinates and forces. */ @@ -888,13 +890,17 @@ void FixColvars::end_of_step() void FixColvars::write_restart(FILE *fp) { if (me == 0) { - std::string rest_text; - proxy->serialize_status(rest_text); - // TODO call write_output_files() - const char *cvm_state = rest_text.c_str(); - int len = strlen(cvm_state) + 1; // need to include terminating null byte. - fwrite(&len,sizeof(int),1,fp); - fwrite(cvm_state,1,len,fp); + cvm::memory_stream ms; + if (proxy->colvars->write_state(ms)) { + int len_cv_state = ms.length(); + // Will write the buffer's length twice, so that the fix can read it later, too + int len = len_cv_state + sizeof(int); + fwrite(&len, sizeof(int), 1, fp); + fwrite(&len, sizeof(int), 1, fp); + fwrite(ms.output_buffer(), 1, len_cv_state, fp); + } else { + error->all(FLERR, "Failed to write Colvars state to binary file"); + } } } @@ -903,8 +909,10 @@ void FixColvars::write_restart(FILE *fp) void FixColvars::restart(char *buf) { if (me == 0) { - std::string rest_text(buf); - if (!proxy->deserialize_status(rest_text)) { + // Read the buffer's length, then load it into Colvars starting right past that location + int length = *(reinterpret_cast(buf)); + unsigned char *colvars_state_buffer = reinterpret_cast(buf + sizeof(int)); + if (proxy->colvars->set_input_state_buffer(length, colvars_state_buffer) != COLVARS_OK) { error->all(FLERR, "Failed to set the Colvars input state from string buffer"); } } diff --git a/lammps/tests/library/run_tests.sh b/lammps/tests/library/run_tests.sh index 499b06b26..e972ce117 100755 --- a/lammps/tests/library/run_tests.sh +++ b/lammps/tests/library/run_tests.sh @@ -229,6 +229,10 @@ for dir in ${DIRLIST} ; do for f in AutoDiff/* do base=`basename $f` + if [ "${base%.state.stripped}" != "${base}" ] && [ -n "${COLVARS_BINARY_RESTART}" ] ; then + # Do not try comparing binary state files, they will never match anyway + continue + fi if [ "${base}" != "${base%.traj}" ] ; then # System force is now total force sed 's/fs_/ft_/g' < ${base} > ${TMPDIR}/${base} diff --git a/misc_interfaces/stubs/colvarproxy_stub.cpp b/misc_interfaces/stubs/colvarproxy_stub.cpp index 576d23a0e..f94273557 100644 --- a/misc_interfaces/stubs/colvarproxy_stub.cpp +++ b/misc_interfaces/stubs/colvarproxy_stub.cpp @@ -7,6 +7,8 @@ // If you wish to distribute your changes, please submit them to the // Colvars repository at GitHub. +#include + #include "colvarmodule.h" #include "colvarscript.h" #include "colvaratoms.h" diff --git a/namd/colvars/Make.depends b/namd/colvars/Make.depends index 54b99dc92..af0333e7b 100644 --- a/namd/colvars/Make.depends +++ b/namd/colvars/Make.depends @@ -7,8 +7,6 @@ obj/colvar.o: \ colvars/src/colvartypes.h \ colvars/src/colvarparse.h \ colvars/src/colvarparams.h \ - colvars/src/colvar.h \ - colvars/src/colvardeps.h \ colvars/src/colvarcomp.h \ colvars/src/colvaratoms.h \ colvars/src/colvarproxy.h \ @@ -16,31 +14,34 @@ obj/colvar.o: \ colvars/src/colvarproxy_system.h \ colvars/src/colvarproxy_tcl.h \ colvars/src/colvarproxy_volmaps.h \ + colvars/src/colvardeps.h \ + colvars/src/colvar.h \ colvars/src/colvar_arithmeticpath.h \ colvars/src/colvar_geometricpath.h \ colvars/src/colvarscript.h \ colvars/src/colvarbias.h \ colvars/src/colvarscript_commands.h \ colvars/src/colvarscript_commands_colvar.h \ - colvars/src/colvarscript_commands_bias.h + colvars/src/colvarscript_commands_bias.h \ + colvars/src/colvars_memstream.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvar.o $(COPTC) colvars/src/colvar.cpp obj/colvaratoms.o: \ obj/.exists \ colvars/src/colvaratoms.cpp \ + colvars/src/colvardeps.h \ colvars/src/colvarmodule.h \ colvars/src/colvars_version.h \ - colvars/src/colvarproxy.h \ - colvars/src/colvartypes.h \ + colvars/src/colvarparse.h \ colvars/src/colvarvalue.h \ + colvars/src/colvartypes.h \ + colvars/src/colvarparams.h \ + colvars/src/colvarproxy.h \ colvars/src/colvarproxy_io.h \ colvars/src/colvarproxy_system.h \ colvars/src/colvarproxy_tcl.h \ colvars/src/colvarproxy_volmaps.h \ - colvars/src/colvarparse.h \ - colvars/src/colvarparams.h \ colvars/src/colvaratoms.h \ - colvars/src/colvardeps.h \ - colvars/src/colvar_rotation_derivative.h + colvars/src/colvar_rotation_derivative.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvaratoms.o $(COPTC) colvars/src/colvaratoms.cpp obj/colvarbias.o: \ obj/.exists \ @@ -87,10 +88,15 @@ obj/colvarbias_alb.o: \ colvars/src/colvarbias_alb.cpp \ colvars/src/colvarmodule.h \ colvars/src/colvars_version.h \ + colvars/src/colvarproxy.h \ + colvars/src/colvartypes.h \ + colvars/src/colvarvalue.h \ + colvars/src/colvarproxy_io.h \ + colvars/src/colvarproxy_system.h \ + colvars/src/colvarproxy_tcl.h \ + colvars/src/colvarproxy_volmaps.h \ colvars/src/colvarbias.h \ colvars/src/colvar.h \ - colvars/src/colvarvalue.h \ - colvars/src/colvartypes.h \ colvars/src/colvarparse.h \ colvars/src/colvarparams.h \ colvars/src/colvardeps.h \ @@ -301,7 +307,7 @@ obj/colvarcomp_distances.o: \ colvars/src/colvarproxy_volmaps.h \ colvars/src/colvar_arithmeticpath.h \ colvars/src/colvar_geometricpath.h \ - colvars/src/colvar_rotation_derivative.h + colvars/src/colvar_rotation_derivative.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvarcomp_distances.o $(COPTC) colvars/src/colvarcomp_distances.cpp obj/colvarcomp_gpath.o: \ obj/.exists \ @@ -365,7 +371,7 @@ obj/colvarcomp_rotations.o: \ colvars/src/colvarproxy_volmaps.h \ colvars/src/colvar_arithmeticpath.h \ colvars/src/colvar_geometricpath.h \ - colvars/src/colvar_rotation_derivative.h + colvars/src/colvar_rotation_derivative.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvarcomp_rotations.o $(COPTC) colvars/src/colvarcomp_rotations.cpp obj/colvarcomp_volmaps.o: \ obj/.exists \ @@ -394,18 +400,18 @@ obj/colvarcomp_combination.o: \ colvars/src/colvarcomp.h \ colvars/src/colvarmodule.h \ colvars/src/colvars_version.h \ - colvars/src/colvar.h \ - colvars/src/colvarvalue.h \ - colvars/src/colvartypes.h \ - colvars/src/colvarparse.h \ - colvars/src/colvarparams.h \ - colvars/src/colvardeps.h \ colvars/src/colvaratoms.h \ colvars/src/colvarproxy.h \ + colvars/src/colvartypes.h \ + colvars/src/colvarvalue.h \ colvars/src/colvarproxy_io.h \ colvars/src/colvarproxy_system.h \ colvars/src/colvarproxy_tcl.h \ colvars/src/colvarproxy_volmaps.h \ + colvars/src/colvarparse.h \ + colvars/src/colvarparams.h \ + colvars/src/colvardeps.h \ + colvars/src/colvar.h \ colvars/src/colvar_arithmeticpath.h \ colvars/src/colvar_geometricpath.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvarcomp_combination.o $(COPTC) colvars/src/colvarcomp_combination.cpp @@ -538,7 +544,8 @@ obj/colvarparse.o: \ colvars/src/colvarvalue.h \ colvars/src/colvartypes.h \ colvars/src/colvarparse.h \ - colvars/src/colvarparams.h + colvars/src/colvarparams.h \ + colvars/src/colvars_memstream.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvarparse.o $(COPTC) colvars/src/colvarparse.cpp obj/colvarproxy.o: \ obj/.exists \ @@ -705,6 +712,15 @@ obj/colvarscript_commands_colvar.o: \ colvars/src/colvarscript_commands_colvar.h \ colvars/src/colvarscript_commands_bias.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvarscript_commands_colvar.o $(COPTC) colvars/src/colvarscript_commands_colvar.cpp +obj/colvars_memstream.o: \ + obj/.exists \ + colvars/src/colvars_memstream.cpp \ + colvars/src/colvarmodule.h \ + colvars/src/colvars_version.h \ + colvars/src/colvartypes.h \ + colvars/src/colvarvalue.h \ + colvars/src/colvars_memstream.h + $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvars_memstream.o $(COPTC) colvars/src/colvars_memstream.cpp obj/colvartypes.o: \ obj/.exists \ colvars/src/colvartypes.cpp \ @@ -714,8 +730,15 @@ obj/colvartypes.o: \ colvars/src/colvarparse.h \ colvars/src/colvarvalue.h \ colvars/src/colvarparams.h \ - colvars/src/nr_jacobi.h \ - colvars/src/colvar_rotation_derivative.h + colvars/src/colvaratoms.h \ + colvars/src/colvarproxy.h \ + colvars/src/colvarproxy_io.h \ + colvars/src/colvarproxy_system.h \ + colvars/src/colvarproxy_tcl.h \ + colvars/src/colvarproxy_volmaps.h \ + colvars/src/colvardeps.h \ + colvars/src/colvar_rotation_derivative.h \ + colvars/src/nr_jacobi.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvartypes.o $(COPTC) colvars/src/colvartypes.cpp obj/colvarvalue.o: \ obj/.exists \ @@ -723,7 +746,8 @@ obj/colvarvalue.o: \ colvars/src/colvarmodule.h \ colvars/src/colvars_version.h \ colvars/src/colvarvalue.h \ - colvars/src/colvartypes.h + colvars/src/colvartypes.h \ + colvars/src/colvars_memstream.h $(CXX) $(COLVARSCXXFLAGS) $(COPTO)obj/colvarvalue.o $(COPTC) colvars/src/colvarvalue.cpp obj/nr_jacobi.o: \ obj/.exists \ diff --git a/namd/colvars/src/Makefile.namd b/namd/colvars/src/Makefile.namd index 93a993c7e..636f29681 100644 --- a/namd/colvars/src/Makefile.namd +++ b/namd/colvars/src/Makefile.namd @@ -36,6 +36,7 @@ COLVARSLIB = \ $(DSTDIR)/colvarscript_commands.o \ $(DSTDIR)/colvarscript_commands_bias.o \ $(DSTDIR)/colvarscript_commands_colvar.o \ + $(DSTDIR)/colvars_memstream.o \ $(DSTDIR)/colvartypes.o \ $(DSTDIR)/colvarvalue.o \ $(DSTDIR)/nr_jacobi.o diff --git a/namd/src/colvarproxy_namd.C b/namd/src/colvarproxy_namd.C index fac6de032..d20bf19dd 100644 --- a/namd/src/colvarproxy_namd.C +++ b/namd/src/colvarproxy_namd.C @@ -1049,7 +1049,7 @@ std::ostream & colvarproxy_namd::output_stream(std::string const &output_name, backup_file(output_name.c_str()); - output_streams_[output_name] = new ofstream_namd(output_name.c_str()); + output_streams_[output_name] = new ofstream_namd(output_name.c_str(), std::ios::binary); if (! output_streams_[output_name]->good()) { cvm::error("Error: cannot write to "+description+" \""+output_name+"\".\n", COLVARS_FILE_ERROR); diff --git a/namd/tests/library/run_tests.sh b/namd/tests/library/run_tests.sh index 8342d4aed..aa7fd7f33 100755 --- a/namd/tests/library/run_tests.sh +++ b/namd/tests/library/run_tests.sh @@ -161,15 +161,18 @@ for dir in ${DIRLIST} ; do script=`basename ${script}` basename=${script%.namd} - # Don't do multithreading for reinitatoms - NUM_THREADS_THIS=${NUM_THREADS} - if [ ${dir} == 003_reinitatoms ] ; then - NUM_THREADS_THIS=1 + # If we are doing binary restarts, make an exception for tests that don't support it + if [ -n "${COLVARS_BINARY_RESTART}" ] ; then + if [ ${dir} == 004_10ala_moving_restart ] ; then + export COLVARS_BINARY_RESTART=0 + else + export COLVARS_BINARY_RESTART=1 + fi fi # Run the test (use a subshell to avoid cluttering stdout) # Use multiple threads to test SMP code (TODO: move SMP tests to interface?) - NAMD_CUDASOA=$CUDASOA $BINARY +p ${NUM_THREADS_THIS} $script > ${basename}.out + NAMD_CUDASOA=$CUDASOA $BINARY +p ${NUM_THREADS} $script > ${basename}.out # Output of Colvars module, minus the version numbers grep "^colvars:" ${basename}.out | grep -v 'Initializing the collective variables module' \ @@ -227,6 +230,10 @@ for dir in ${DIRLIST} ; do for f in AutoDiff/* do base=`basename $f` + if [ "${base%.state.stripped}" != "${base}" ] && [ -n "${COLVARS_BINARY_RESTART}" ] ; then + # Do not try comparing binary state files, they will never match anyway + continue + fi if [ ! -f $base ] ; then echo -e "\n*** File $(${TPUT_RED})$base$(${TPUT_CLEAR}) is missing. ***" SUCCESS=0 diff --git a/src/.clang-format b/src/.clang-format index e05242256..66e78c8b5 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -8,4 +8,7 @@ ObjCBlockIndentWidth: 2 PenaltyBreakAssignment: 2 AccessModifierOffset: -2 BreakBeforeBraces: WebKit +EmptyLineAfterAccessModifier: Leave +NamespaceIndentation: None +MaxEmptyLinesToKeep: 2 ... diff --git a/src/colvar.cpp b/src/colvar.cpp index efa7242e2..b90350737 100644 --- a/src/colvar.cpp +++ b/src/colvar.cpp @@ -19,6 +19,7 @@ #include "colvarcomp.h" #include "colvar.h" #include "colvarscript.h" +#include "colvars_memstream.h" std::map> @@ -37,6 +38,8 @@ colvar::colvar() dev_null = 0.0; #endif + matching_state = false; + expand_boundaries = false; description = "uninitialized colvar"; @@ -2275,44 +2278,65 @@ void colvar::wrap(colvarvalue &x_unwrapped) const std::istream & colvar::read_state(std::istream &is) { - std::streampos const start_pos = is.tellg(); + auto const start_pos = is.tellg(); std::string conf; - if ( !(is >> colvarparse::read_block("colvar", &conf)) ) { + if ( !(is >> colvarparse::read_block("colvar", &conf)) || + (check_matching_state(conf) != COLVARS_OK) ) { // this is not a colvar block is.clear(); - is.seekg(start_pos, std::ios::beg); + is.seekg(start_pos); is.setstate(std::ios::failbit); return is; } - { - std::string check_name = ""; - get_keyval(conf, "name", check_name, - std::string(""), colvarparse::parse_silent); - if (check_name.size() == 0) { - cvm::error("Error: Collective variable in the " - "restart file without any identifier.\n", COLVARS_INPUT_ERROR); - is.clear(); - is.seekg(start_pos, std::ios::beg); - is.setstate(std::ios::failbit); - return is; - } + if (!matching_state) { + // No errors reading, but this state is not for this colvar; rewind + is.seekg(start_pos); + return is; + } - if (check_name != name) { - if (cvm::debug()) { - cvm::log("Ignoring state of colvar \""+check_name+ - "\": this colvar is named \""+name+"\".\n"); - } - is.seekg(start_pos, std::ios::beg); - return is; + if (set_state_params(conf) != COLVARS_OK) { + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::failbit); + } + + return is; +} + + +int colvar::check_matching_state(std::string const &conf) +{ + std::string check_name = ""; + get_keyval(conf, "name", check_name, std::string(""), colvarparse::parse_silent); + + if (check_name.size() == 0) { + return cvm::error("Error: Collective variable in the " + "state file without any identifier.\n", COLVARS_INPUT_ERROR); + } + + if (check_name != name) { + if (cvm::debug()) { + cvm::log("Ignoring state of colvar \""+check_name+ + "\": this colvar is named \""+name+"\".\n"); } + matching_state = false; + } else { + matching_state = true; } + return COLVARS_OK; +} + + +int colvar::set_state_params(std::string const &conf) +{ + int error_code = COLVARS_OK; if ( !(get_keyval(conf, "x", x, x, colvarparse::parse_silent)) ) { - cvm::log("Error: restart file does not contain " - "the value of the colvar \""+ - name+"\" .\n"); + error_code |= cvm::error("Error: restart file does not contain " + "the value of the colvar \""+ + name+"\" .\n", COLVARS_INPUT_ERROR); } else { cvm::log("Restarting collective variable \""+name+"\" from value: "+ cvm::to_str(x)+"\n"); @@ -2325,9 +2349,10 @@ std::istream & colvar::read_state(std::istream &is) colvarvalue(x.type()), colvarparse::parse_silent)) || !(get_keyval(conf, "extended_v", v_ext, colvarvalue(x.type()), colvarparse::parse_silent)) ) { - cvm::log("Error: restart file does not contain " - "\"extended_x\" or \"extended_v\" for the colvar \""+ - name+"\", but you requested \"extendedLagrangian\".\n"); + error_code |= cvm::error("Error: restart file does not contain " + "\"extended_x\" or \"extended_v\" for the colvar \""+ + name+"\", but you requested \"extendedLagrangian\".\n", + COLVARS_INPUT_ERROR); } x_reported = x_ext; } else { @@ -2338,9 +2363,10 @@ std::istream & colvar::read_state(std::istream &is) if ( !(get_keyval(conf, "v", v_fdiff, colvarvalue(x.type()), colvarparse::parse_silent)) ) { - cvm::log("Error: restart file does not contain " - "the velocity for the colvar \""+ - name+"\", but you requested \"outputVelocity\".\n"); + error_code |= cvm::error("Error: restart file does not contain " + "the velocity for the colvar \""+ + name+"\", but you requested \"outputVelocity\".\n", + COLVARS_INPUT_ERROR); } if (is_enabled(f_cv_extended_Lagrangian)) { @@ -2350,6 +2376,41 @@ std::istream & colvar::read_state(std::istream &is) } } + return error_code; +} + + +cvm::memory_stream &colvar::read_state(cvm::memory_stream &is) +{ + auto const start_pos = is.tellg(); + std::string key, data; + if (is >> key) { + if (key == "colvar") { + // Read a formatted config string, then read the state parameters from it + if (is >> data) { + if (set_state_params(data) == COLVARS_OK) { + return is; + } + } + } + } + + auto const error_pos = is.tellg(); + + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::failbit); + + std::string error_msg("Error: in reading state data for colvar \"" + name + " at position " + + cvm::to_str(error_pos) + " in unformatted stream.\n"); + if (key.size() && key != "colvar") { + error_msg += "; the keyword read was \"" + key + "\", but \"colvar\" was expected"; + } + if (data.size()) { + error_msg += "; the configuration string read was not recognized"; + } + error_msg += ".\n"; + cvm::error(error_msg, COLVARS_INPUT_ERROR); return is; } @@ -2364,7 +2425,7 @@ std::istream & colvar::read_traj(std::istream &is) cvm::log("Error: in reading the value of colvar \""+ this->name+"\" from trajectory.\n"); is.clear(); - is.seekg(start_pos, std::ios::beg); + is.seekg(start_pos); is.setstate(std::ios::failbit); return is; } @@ -2404,10 +2465,23 @@ std::istream & colvar::read_traj(std::istream &is) // ******************** OUTPUT FUNCTIONS ******************** -std::ostream & colvar::write_state(std::ostream &os) { +std::ostream & colvar::write_state(std::ostream &os) const +{ + os << "colvar {\n" << get_state_params() << "}\n\n"; + + if (runave_outfile.size() > 0) { + cvm::main()->proxy->flush_output_stream(runave_outfile); + } + + return os; +} - os << "colvar {\n" - << " name " << name << "\n" + +std::string const colvar::get_state_params() const +{ + std::ostringstream os; + + os << " name " << name << "\n" << " x " << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width) @@ -2431,7 +2505,13 @@ std::ostream & colvar::write_state(std::ostream &os) { << v_reported << "\n"; } - os << "}\n\n"; + return os.str(); +} + + +cvm::memory_stream & colvar::write_state(cvm::memory_stream &os) const +{ + os << std::string("colvar") << get_state_params(); if (runave_outfile.size() > 0) { cvm::main()->proxy->flush_output_stream(runave_outfile); diff --git a/src/colvar.h b/src/colvar.h index 7150cf03d..4dee696ce 100644 --- a/src/colvar.h +++ b/src/colvar.h @@ -10,9 +10,9 @@ #ifndef COLVAR_H #define COLVAR_H -#include -#include #include +#include +#include #include "colvarmodule.h" #include "colvarvalue.h" @@ -456,16 +456,35 @@ class colvar : public colvarparse, public colvardeps { /// Write a label to the trajectory file (comment line) std::ostream & write_traj_label(std::ostream &os); - /// Read the collective variable from a restart file + /// Read the colvar's state from a formatted input stream std::istream & read_state(std::istream &is); - /// Write the collective variable to a restart file - std::ostream & write_state(std::ostream &os); + + /// Read the colvar's state from an unformatted input stream + cvm::memory_stream & read_state(cvm::memory_stream &is); + + /// Check the name of the bias vs. the given string, set the matching_state flag accordingly + int check_matching_state(std::string const &state_conf); + + /// Read the values of colvar mutable data from a string (used by both versions of read_state()) + int set_state_params(std::string const &state_conf); + + /// Write the state information of this colvar in a block of text, suitable for later parsing + std::string const get_state_params() const; + + /// Write the colvar's state to a formatted output stream + std::ostream & write_state(std::ostream &os) const; + + /// Write the colvar's state to an unformatted output stream + cvm::memory_stream & write_state(cvm::memory_stream &os) const; /// Write output files (if defined, e.g. in analysis mode) int write_output_files(); protected: + /// Flag used to tell if the state string being read is for this colvar + bool matching_state; + /// Previous value (to calculate velocities during analysis) colvarvalue x_old; @@ -758,4 +777,3 @@ inline void colvar::reset_bias_force() { } #endif - diff --git a/src/colvar_geometricpath.h b/src/colvar_geometricpath.h index 9ff78261f..51f97bb67 100644 --- a/src/colvar_geometricpath.h +++ b/src/colvar_geometricpath.h @@ -8,12 +8,13 @@ // Colvars repository at GitHub. -#include "colvarmodule.h" - -#include -#include #include +#include #include +#include + +#include "colvarmodule.h" + namespace GeometricPathCV { @@ -171,10 +172,14 @@ void GeometricPathBase::determineClosestFr sign = -1; } if (cvm::fabs(static_cast(frame_index[0]) - static_cast(frame_index[1])) > 1) { - std::cout << "Warning: Geometrical pathCV relies on the assumption that the second closest frame is the neighbouring frame\n"; - std::cout << " Please check your configuration or increase restraint on z(σ)\n"; + std::string message( + "Warning: Geometrical pathCV relies on the assumption that the second closest frame is " + "the neighbouring frame\n" + " Please check your configuration or increase restraint on z(σ)\n"); for (size_t i_frame = 0; i_frame < frame_index.size(); ++i_frame) { - std::cout << "Frame index: " << frame_index[i_frame] << " ; optimal RMSD = " << frame_distances[frame_index[i_frame]] << "\n"; + message += "Frame index: " + cvm::to_str(frame_index[i_frame]) + + " ; optimal RMSD = " + cvm::to_str(frame_distances[frame_index[i_frame]]) + + "\n"; } } min_frame_index_1 = frame_index[0]; // s_m diff --git a/src/colvarbias.cpp b/src/colvarbias.cpp index 653804a4b..169fc073b 100644 --- a/src/colvarbias.cpp +++ b/src/colvarbias.cpp @@ -15,6 +15,7 @@ #include "colvarvalue.h" #include "colvarbias.h" #include "colvargrid.h" +#include "colvars_memstream.h" colvarbias::colvarbias(char const *key) @@ -499,68 +500,98 @@ int colvarbias::set_state_params(std::string const &conf) std::ostream & colvarbias::write_state(std::ostream &os) { if (cvm::debug()) { - cvm::log("Writing state file for bias \""+name+"\"\n"); + cvm::log("Writing formatted state for bias \""+name+"\"\n"); } os.setf(std::ios::scientific, std::ios::floatfield); os.precision(cvm::cv_prec); os << state_keyword << " {\n" - << " configuration {\n"; - std::istringstream is(get_state_params()); - std::string line; - while (std::getline(is, line)) { - os << " " << line << "\n"; - } - os << " }\n"; + << " configuration {\n" + << get_state_params() + << " }\n"; write_state_data(os); os << "}\n\n"; return os; } -std::istream & colvarbias::read_state(std::istream &is) +cvm::memory_stream & colvarbias::write_state(cvm::memory_stream &os) +{ + if (cvm::debug()) { + cvm::log("Writing unformatted state for bias \""+name+"\"\n"); + } + os << state_keyword << std::string("configuration") << get_state_params(); + write_state_data(os); + return os; +} + + +template +void raise_error_rewind(IST &is, SPT start_pos, std::string const &bias_type, + std::string const &bias_name, std::string const added_msg = "") +{ + auto state = is.rdstate(); + is.clear(); + is.seekg(start_pos); + is.setstate(state | std::ios::failbit); + cvm::error("Error: in reading state for \"" + bias_type + "\" bias \"" + bias_name + + "\" at position " + cvm::to_str(static_cast(is.tellg())) + " in stream." + + added_msg + "\n", + COLVARS_INPUT_ERROR); +} + + +template IST & colvarbias::read_state_template_(IST &is) { auto const start_pos = is.tellg(); std::string key, brace, conf; - if ( !(is >> key) || !(key == state_keyword || key == bias_type) || - !(is >> brace) || !(brace == "{") || - !(is >> colvarparse::read_block("configuration", &conf)) || - (check_matching_state(conf) != COLVARS_OK) ) { - cvm::error("Error: in reading state configuration for \""+bias_type+ - "\" bias \""+ - this->name+"\" at position "+ - cvm::to_str(static_cast(is.tellg()))+ - " in stream.\n", COLVARS_INPUT_ERROR); - is.clear(); - is.seekg(start_pos); - is.setstate(std::ios::failbit); + if (is >> key) { + if (key == state_keyword || key == bias_type) { + + if (! std::is_same::value) { + // Formatted input only + if (!(is >> brace) || !(brace == "{") ) { + raise_error_rewind(is, start_pos, bias_type, name); + return is; + } + } + + if (!(is >> colvarparse::read_block("configuration", &conf)) || + (check_matching_state(conf) != COLVARS_OK)) { + raise_error_rewind(is, start_pos, bias_type, name); + return is; + } + + } else { + // Not a match for this bias type, rewind without error + is.seekg(start_pos); + return is; + } + + } else { + raise_error_rewind(is, start_pos, bias_type, name); return is; } if (!matching_state) { - // No errors reading, but this state is not for this bias; rewind + // No errors, but not a match for this bias instance; rewind is.seekg(start_pos); return is; } if ((set_state_params(conf) != COLVARS_OK) || !read_state_data(is)) { - cvm::error("Error: in reading state data for \""+bias_type+"\" bias \""+ - this->name+"\" at position "+ - cvm::to_str(static_cast(is.tellg()))+ - " in stream.\n", COLVARS_INPUT_ERROR); - auto state = is.rdstate(); - is.clear(); - is.seekg(start_pos); - is.setstate(state); + raise_error_rewind(is, start_pos, bias_type, name); } - is >> brace; - if (brace != "}") { - cvm::error("Error: corrupt restart information for \""+bias_type+"\" bias \""+ - this->name+"\": no matching brace at position "+ - cvm::to_str(static_cast(is.tellg()))+ - " in stream.\n"); - is.setstate(std::ios::failbit); + if (! std::is_same::value) { + is >> brace; + if (brace != "}") { + cvm::error("Error: corrupt restart information for \""+bias_type+"\" bias \""+ + this->name+"\": no matching brace at position "+ + cvm::to_str(static_cast(is.tellg()))+ + " in stream.\n"); + raise_error_rewind(is, start_pos, bias_type, name); + } } cvm::log("Restarted " + bias_type + " bias \"" + name + "\" with step number " + @@ -570,6 +601,18 @@ std::istream & colvarbias::read_state(std::istream &is) } +std::istream &colvarbias::read_state(std::istream &is) +{ + return read_state_template_(is); +} + + +cvm::memory_stream &colvarbias::read_state(cvm::memory_stream &is) +{ + return read_state_template_(is); +} + + int colvarbias::write_state_prefix(std::string const &prefix) { std::string const filename = @@ -640,25 +683,51 @@ int colvarbias::read_state_string(char const *buffer) } -std::istream & colvarbias::read_state_data_key(std::istream &is, char const *key) +std::ostream &colvarbias::write_state_data_key(std::ostream &os, std::string const &key, + bool header) +{ + os << (header ? "\n" : "") << key << (header ? "\n" : " "); + return os; +} + + +cvm::memory_stream &colvarbias::write_state_data_key(cvm::memory_stream &os, std::string const &key, + bool /* header */) { - std::streampos const start_pos = is.tellg(); + os << std::string(key); + return os; +} + + +template +IST &colvarbias::read_state_data_key_template_(IST &is, std::string const &key) +{ + auto const start_pos = is.tellg(); std::string key_in; - if ( !(is >> key_in) || - !(to_lower_cppstr(key_in) == to_lower_cppstr(std::string(key))) ) { - cvm::error("Error: in reading restart configuration for "+ - bias_type+" bias \""+this->name+"\" at position "+ - cvm::to_str(static_cast(is.tellg()))+ - " in stream.\n", COLVARS_INPUT_ERROR); - is.clear(); - is.seekg(start_pos, std::ios::beg); - is.setstate(std::ios::failbit); - return is; + if (is >> key_in) { + if (key_in != key) { + raise_error_rewind(is, start_pos, bias_type, name, + " Expected keyword \"" + std::string(key) + "\", found \"" + key_in + + "\"."); + } + } else { + raise_error_rewind(is, start_pos, bias_type, name); } return is; } +std::istream & colvarbias::read_state_data_key(std::istream &is, std::string const &key) +{ + return read_state_data_key_template_(is, key); +} + + +cvm::memory_stream & colvarbias::read_state_data_key(cvm::memory_stream &is, std::string const &key) +{ + return read_state_data_key_template_(is, key); +} + std::ostream & colvarbias::write_traj_label(std::ostream &os) { @@ -865,9 +934,22 @@ std::ostream & colvarbias_ti::write_state_data(std::ostream &os) if (! is_enabled(f_cvb_calc_ti_samples)) { return os; } - os << "\nhistogram\n"; + write_state_data_key(os, "histogram"); ti_count->write_raw(os); - os << "\nsystem_forces\n"; + write_state_data_key(os, "system_forces"); + ti_avg_forces->write_raw(os); + return os; +} + + +cvm::memory_stream & colvarbias_ti::write_state_data(cvm::memory_stream &os) +{ + if (! is_enabled(f_cvb_calc_ti_samples)) { + return os; + } + write_state_data_key(os, "histogram"); + ti_count->write_raw(os); + write_state_data_key(os, "system_forces"); ti_avg_forces->write_raw(os); return os; } @@ -900,6 +982,33 @@ std::istream & colvarbias_ti::read_state_data(std::istream &is) } +cvm::memory_stream & colvarbias_ti::read_state_data(cvm::memory_stream &is) +{ + if (! is_enabled(f_cvb_calc_ti_samples)) { + return is; + } + if (cvm::debug()) { + cvm::log("Reading state data for the TI estimator.\n"); + } + if (! read_state_data_key(is, "histogram")) { + return is; + } + if (! ti_count->read_raw(is)) { + return is; + } + if (! read_state_data_key(is, "system_forces")) { + return is; + } + if (! ti_avg_forces->read_raw(is)) { + return is; + } + if (cvm::debug()) { + cvm::log("Done reading state data for the TI estimator.\n"); + } + return is; +} + + int colvarbias_ti::write_output_files() { int error_code = COLVARS_OK; diff --git a/src/colvarbias.h b/src/colvarbias.h index de7b04b51..9a51f9d51 100644 --- a/src/colvarbias.h +++ b/src/colvarbias.h @@ -139,29 +139,81 @@ class colvarbias /// Read the values of specific mutable properties from a string virtual int set_state_params(std::string const &state_conf); - /// Write all mutable data not already written by get_state_params() + /// Write all mutable data not already written by get_state_params() to a formatted stream virtual std::ostream & write_state_data(std::ostream &os) { return os; } - /// Read all mutable data not already set by set_state_params() + /// Write all mutable data not already written by get_state_params() to an unformatted stream + virtual cvm::memory_stream & write_state_data(cvm::memory_stream &os) + { + return os; + } + + /// Read all mutable data not already set by set_state_params() from a formatted stream virtual std::istream & read_state_data(std::istream &is) { return is; } - /// Read a keyword from the state data (typically a header) - /// \param Input stream - /// \param Keyword labeling the header block - std::istream & read_state_data_key(std::istream &is, char const *key); + /// Read all mutable data not already set by set_state_params() from an unformatted stream + virtual cvm::memory_stream & read_state_data(cvm::memory_stream &is) + { + return is; + } + + /// Write a keyword header for a data sequence to a formatted stream + /// \param[in,out] os Output stream + /// \param[in] key Keyword labeling the header block + /// \param[in] header Whether this is the header of a multi-line segment vs a single line + std::ostream &write_state_data_key(std::ostream &os, std::string const &key, bool header = true); + + /// Write a keyword header for a data sequence to an unformatted stream + /// \param[in,out] os Output stream + /// \param[in] key Keyword labeling the header block + /// \param[in] header Ignored + cvm::memory_stream &write_state_data_key(cvm::memory_stream &os, std::string const &key, + bool header = true); - /// Write the bias configuration to a state file or other stream - std::ostream & write_state(std::ostream &os); +private: + + /// Read a keyword header for a data sequence from a stream + /// \param[in,out] Input stream + /// \param[in] Keyword labeling the header block; an error will be raised if not matching + template IST &read_state_data_key_template_(IST &is, std::string const &key); - /// Read the bias configuration from a restart file or other stream +public: + + /// Read a keyword header for a data sequence from a formatted stream + /// \param[in,out] Input stream + /// \param[in] Keyword labeling the header block; an error will be raised if not matching + std::istream & read_state_data_key(std::istream &is, std::string const &key); + + /// Read a keyword header for a data sequence from an unformatted stream + /// \param[in,out] Input stream + /// \param[in] Keyword labeling the header block; an error will be raised if not matching + cvm::memory_stream & read_state_data_key(cvm::memory_stream &is, std::string const &key); + +private: + + /// Generic stream reading function (formatted and not) + template IST & read_state_template_(IST &is); + +public: + + /// Write the bias configuration to a formatted stream + std::ostream &write_state(std::ostream &os); + + /// Write the bias configuration to an unformatted stream + cvm::memory_stream & write_state(cvm::memory_stream &os); + + /// Read the bias configuration from a formatted stream std::istream & read_state(std::istream &is); + /// Read the bias configuration from an unformatted stream + cvm::memory_stream & read_state(cvm::memory_stream &is); + /// Write the bias state to a file with the given prefix int write_state_prefix(std::string const &prefix); @@ -291,7 +343,9 @@ class colvarbias_ti : public virtual colvarbias { virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &state_conf); virtual std::ostream & write_state_data(std::ostream &os); + virtual cvm::memory_stream & write_state_data(cvm::memory_stream &os); virtual std::istream & read_state_data(std::istream &is); + virtual cvm::memory_stream & read_state_data(cvm::memory_stream &is); virtual int write_output_files(); protected: diff --git a/src/colvarbias_abf.cpp b/src/colvarbias_abf.cpp index 7d6b7b7fe..acca85efa 100644 --- a/src/colvarbias_abf.cpp +++ b/src/colvarbias_abf.cpp @@ -12,6 +12,7 @@ #include "colvarmodule.h" #include "colvar.h" #include "colvarbias_abf.h" +#include "colvars_memstream.h" colvarbias_abf::colvarbias_abf(char const *key) @@ -741,24 +742,24 @@ int colvarbias_abf::read_gradients_samples() } -std::ostream & colvarbias_abf::write_state_data(std::ostream& os) +template OST & colvarbias_abf::write_state_data_template_(OST &os) { - std::ios::fmtflags flags(os.flags()); + auto flags = os.flags(); os.setf(std::ios::fmtflags(0), std::ios::floatfield); // default floating-point format - os << "\nsamples\n"; + write_state_data_key(os, "samples"); samples->write_raw(os, 8); os.flags(flags); - os << "\ngradient\n"; + write_state_data_key(os, "gradient"); gradients->write_raw(os, 8); if (b_CZAR_estimator) { os.setf(std::ios::fmtflags(0), std::ios::floatfield); // default floating-point format - os << "\nz_samples\n"; + write_state_data_key(os, "z_samples"); z_samples->write_raw(os, 8); os.flags(flags); - os << "\nz_gradient\n"; + write_state_data_key(os, "z_gradient"); z_gradients->write_raw(os, 8); } @@ -767,7 +768,19 @@ std::ostream & colvarbias_abf::write_state_data(std::ostream& os) } -std::istream & colvarbias_abf::read_state_data(std::istream& is) +std::ostream & colvarbias_abf::write_state_data(std::ostream& os) +{ + return write_state_data_template_(os); +} + + +cvm::memory_stream & colvarbias_abf::write_state_data(cvm::memory_stream& os) +{ + return write_state_data_template_(os); +} + + +template IST &colvarbias_abf::read_state_data_template_(IST &is) { if ( input_prefix.size() > 0 ) { cvm::error("ERROR: cannot provide both inputPrefix and a colvars state file.\n", COLVARS_INPUT_ERROR); @@ -812,6 +825,18 @@ std::istream & colvarbias_abf::read_state_data(std::istream& is) } +std::istream & colvarbias_abf::read_state_data(std::istream& is) +{ + return read_state_data_template_(is); +} + + +cvm::memory_stream & colvarbias_abf::read_state_data(cvm::memory_stream& is) +{ + return read_state_data_template_(is); +} + + int colvarbias_abf::write_output_files() { if (cvm::debug()) { diff --git a/src/colvarbias_abf.h b/src/colvarbias_abf.h index 9eae867a7..635e0fbfd 100644 --- a/src/colvarbias_abf.h +++ b/src/colvarbias_abf.h @@ -159,8 +159,24 @@ class colvarbias_abf : public colvarbias { std::string const &name, bool close); - virtual std::istream& read_state_data(std::istream&); - virtual std::ostream& write_state_data(std::ostream&); +private: + + /// Generic stream writing function (formatted and not) + template OST &write_state_data_template_(OST &os); + + /// Generic stream readingx function (formatted and not) + template IST &read_state_data_template_(IST &is); + +public: + + virtual std::ostream &write_state_data(std::ostream &os); + + virtual cvm::memory_stream &write_state_data(cvm::memory_stream &os); + + virtual std::istream &read_state_data(std::istream &is); + + virtual cvm::memory_stream &read_state_data(cvm::memory_stream &is); + virtual int write_output_files(); /// Calculate the bias energy for 1D ABF diff --git a/src/colvarbias_histogram.cpp b/src/colvarbias_histogram.cpp index 84f1a5bde..640bec353 100644 --- a/src/colvarbias_histogram.cpp +++ b/src/colvarbias_histogram.cpp @@ -7,10 +7,13 @@ // If you wish to distribute your changes, please submit them to the // Colvars repository at GitHub. +#include + #include "colvarmodule.h" #include "colvarproxy.h" #include "colvar.h" #include "colvarbias_histogram.h" +#include "colvars_memstream.h" colvarbias_histogram::colvarbias_histogram(char const *key) @@ -197,13 +200,18 @@ int colvarbias_histogram::write_output_files() std::istream & colvarbias_histogram::read_state_data(std::istream& is) { - if (! read_state_data_key(is, "grid")) { - return is; - } - if (! grid->read_raw(is)) { - return is; + if (read_state_data_key(is, "grid")) { + grid->read_raw(is); } + return is; +} + +cvm::memory_stream & colvarbias_histogram::read_state_data(cvm::memory_stream& is) +{ + if (read_state_data_key(is, "grid")) { + grid->read_raw(is); + } return is; } @@ -212,8 +220,16 @@ std::ostream & colvarbias_histogram::write_state_data(std::ostream& os) { std::ios::fmtflags flags(os.flags()); os.setf(std::ios::fmtflags(0), std::ios::floatfield); - os << "grid\n"; + write_state_data_key(os, "grid"); grid->write_raw(os, 8); os.flags(flags); return os; } + + +cvm::memory_stream & colvarbias_histogram::write_state_data(cvm::memory_stream& os) +{ + write_state_data_key(os, "grid"); + grid->write_raw(os); + return os; +} diff --git a/src/colvarbias_histogram.h b/src/colvarbias_histogram.h index 6044fa189..2c6ee84d1 100644 --- a/src/colvarbias_histogram.h +++ b/src/colvarbias_histogram.h @@ -24,11 +24,16 @@ class colvarbias_histogram : public colvarbias { public: colvarbias_histogram(char const *key); - ~colvarbias_histogram(); + virtual ~colvarbias_histogram(); virtual int init(std::string const &conf); virtual int update(); virtual int write_output_files(); + virtual std::ostream & write_state_data(std::ostream &os); + virtual cvm::memory_stream & write_state_data(cvm::memory_stream &os); + virtual std::istream & read_state_data(std::istream &is); + virtual cvm::memory_stream & read_state_data(cvm::memory_stream &is); + protected: /// n-dim histogram @@ -40,9 +45,6 @@ class colvarbias_histogram : public colvarbias { size_t colvar_array_size; /// If colvar_array_size is larger than 1, weigh each one by this number before accumulating the histogram std::vector weights; - - virtual std::istream & read_state_data(std::istream &is); - virtual std::ostream & write_state_data(std::ostream &os); }; #endif diff --git a/src/colvarbias_histogram_reweight_amd.cpp b/src/colvarbias_histogram_reweight_amd.cpp index 85f1bde35..de2f6d9b8 100644 --- a/src/colvarbias_histogram_reweight_amd.cpp +++ b/src/colvarbias_histogram_reweight_amd.cpp @@ -9,6 +9,7 @@ #include "colvarbias_histogram_reweight_amd.h" #include "colvarproxy.h" +#include "colvars_memstream.h" colvarbias_reweightaMD::colvarbias_reweightaMD(char const *key) : colvarbias_histogram(key), grid_count(NULL), grid_dV(NULL), @@ -343,23 +344,37 @@ void colvarbias_reweightaMD::compute_cumulant_expansion_factor( } } -std::ostream & colvarbias_reweightaMD::write_state_data(std::ostream& os) + +template OST & colvarbias_reweightaMD::write_state_data_template_(OST& os) { std::ios::fmtflags flags(os.flags()); os.setf(std::ios::fmtflags(0), std::ios::floatfield); - os << "grid\n"; + write_state_data_key(os, "grid"); grid->write_raw(os, 8); - os << "grid_count\n"; + write_state_data_key(os, "grid_count"); grid_count->write_raw(os, 8); - os << "grid_dV\n"; + write_state_data_key(os, "grid_dV"); grid_dV->write_raw(os, 8); - os << "grid_dV_square\n"; + write_state_data_key(os, "grid_dV_square"); grid_dV_square->write_raw(os, 8); os.flags(flags); return os; } -std::istream & colvarbias_reweightaMD::read_state_data(std::istream& is) + +std::ostream & colvarbias_reweightaMD::write_state_data(std::ostream& os) +{ + return write_state_data_template_(os); +} + + +cvm::memory_stream & colvarbias_reweightaMD::write_state_data(cvm::memory_stream& os) +{ + return write_state_data_template_(os); +} + + +template IST & colvarbias_reweightaMD::read_state_data_template_(IST& is) { if (! read_state_data_key(is, "grid")) { return is; @@ -387,3 +402,15 @@ std::istream & colvarbias_reweightaMD::read_state_data(std::istream& is) } return is; } + + +std::istream & colvarbias_reweightaMD::read_state_data(std::istream& is) +{ + return read_state_data_template_(is); +} + + +cvm::memory_stream & colvarbias_reweightaMD::read_state_data(cvm::memory_stream& is) +{ + return read_state_data_template_(is); +} diff --git a/src/colvarbias_histogram_reweight_amd.h b/src/colvarbias_histogram_reweight_amd.h index 7f50eb0f3..43759b3bd 100644 --- a/src/colvarbias_histogram_reweight_amd.h +++ b/src/colvarbias_histogram_reweight_amd.h @@ -79,9 +79,14 @@ class colvarbias_reweightaMD : public colvarbias_histogram { /// Write gradients of the PMF? bool b_write_gradients; + template OST & write_state_data_template_(OST& os); + template IST & read_state_data_template_(IST& is); + /// save and restore virtual std::istream & read_state_data(std::istream &is) override; + virtual cvm::memory_stream & read_state_data(cvm::memory_stream &is) override; virtual std::ostream & write_state_data(std::ostream &os) override; + virtual cvm::memory_stream & write_state_data(cvm::memory_stream &os) override; private: /// temporary grids for evaluating PMFs diff --git a/src/colvarbias_meta.cpp b/src/colvarbias_meta.cpp index 0d2634533..b00b2482c 100644 --- a/src/colvarbias_meta.cpp +++ b/src/colvarbias_meta.cpp @@ -35,6 +35,7 @@ #include "colvarproxy.h" #include "colvar.h" #include "colvarbias_meta.h" +#include "colvars_memstream.h" colvarbias_meta::colvarbias_meta(char const *key) @@ -625,7 +626,7 @@ int colvarbias_meta::update_bias() std::ostream &replica_hills_os = cvm::proxy->output_stream(replica_hills_file); if (replica_hills_os) { - replica_hills_os << hills.back(); + write_hill(replica_hills_os, hills.back()); } else { return cvm::error("Error: in metadynamics bias \""+this->name+"\""+ ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ @@ -1298,20 +1299,39 @@ int colvarbias_meta::set_state_params(std::string const &state_conf) } -std::istream & colvarbias_meta::read_state_data(std::istream& is) +template +IST & colvarbias_meta::read_grid_data_template_(IST& is, std::string const &key, + GT *grid, GT *backup_grid) { - if (use_grids) { - - if (expand_grids) { - // the boundaries of the colvars may have been changed; TODO: - // this reallocation is only for backward-compatibility, and may - // be deleted when grid_parameters (i.e. colvargrid's own - // internal reallocation) has kicked in - delete hills_energy; - delete hills_energy_gradients; - hills_energy = new colvar_grid_scalar(colvars); - hills_energy_gradients = new colvar_grid_gradient(colvars); + auto const start_pos = is.tellg(); + std::string key_in; + if (is >> key_in) { + if ((key != key_in) || !(grid->read_restart(is))) { + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::failbit); + if (!rebin_grids) { + if ((backup_grid == nullptr) || (comm == single_replica)) { + cvm::error("Error: couldn't read grid data for metadynamics bias \""+ + this->name+"\""+ + ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ + "; if useGrids was off when the state file was written, " + "try enabling rebinGrids now to regenerate the grids.\n", COLVARS_INPUT_ERROR); + } + } } + } else { + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::failbit); + } + return is; +} + + +template IST &colvarbias_meta::read_state_data_template_(IST &is) +{ + if (use_grids) { colvar_grid_scalar *hills_energy_backup = NULL; colvar_grid_gradient *hills_energy_gradients_backup = NULL; @@ -1327,95 +1347,26 @@ std::istream & colvarbias_meta::read_state_data(std::istream& is) hills_energy_gradients = new colvar_grid_gradient(colvars); } - std::streampos const hills_energy_pos = is.tellg(); - std::string key; - if (!(is >> key)) { - if (hills_energy_backup != NULL) { - delete hills_energy; - delete hills_energy_gradients; - hills_energy = hills_energy_backup; - hills_energy_gradients = hills_energy_gradients_backup; - } - is.clear(); - is.seekg(hills_energy_pos, std::ios::beg); - is.setstate(std::ios::failbit); - return is; - } else if (!(key == std::string("hills_energy")) || - !(hills_energy->read_restart(is))) { - is.clear(); - is.seekg(hills_energy_pos, std::ios::beg); - if (!rebin_grids) { - if ((hills_energy_backup == NULL) || (comm == single_replica)) { - cvm::error("Error: couldn't read the energy grid for metadynamics bias \""+ - this->name+"\""+ - ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ - "; if useGrids was off when the state file was written, " - "enable rebinGrids now to regenerate the grids.\n"); - } else { - delete hills_energy; - delete hills_energy_gradients; - hills_energy = hills_energy_backup; - hills_energy_gradients = hills_energy_gradients_backup; - is.setstate(std::ios::failbit); - return is; - } - } - } + read_grid_data_template_(is, "hills_energy", hills_energy, + hills_energy_backup); - std::streampos const hills_energy_gradients_pos = is.tellg(); - if (!(is >> key)) { - if (hills_energy_backup != NULL) { - delete hills_energy; - delete hills_energy_gradients; - hills_energy = hills_energy_backup; - hills_energy_gradients = hills_energy_gradients_backup; + read_grid_data_template_( + is, "hills_energy_gradients", hills_energy_gradients, hills_energy_gradients_backup); + + if (is) { + cvm::log(" successfully read the biasing potential and its gradients from grids.\n"); + if (hills_energy_backup != nullptr) { + // Now that we have successfully updated the grids, delete the backup copies + delete hills_energy_backup; + delete hills_energy_gradients_backup; } - is.clear(); - is.seekg(hills_energy_gradients_pos, std::ios::beg); - is.setstate(std::ios::failbit); + } else { return is; - } else if (!(key == std::string("hills_energy_gradients")) || - !(hills_energy_gradients->read_restart(is))) { - is.clear(); - is.seekg(hills_energy_gradients_pos, std::ios::beg); - if (!rebin_grids) { - if ((hills_energy_backup == NULL) || (comm == single_replica)) { - cvm::error("Error: couldn't read the gradients grid for metadynamics bias \""+ - this->name+"\""+ - ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+ - "; if useGrids was off when the state file was written, " - "enable rebinGrids now to regenerate the grids.\n"); - } else { - delete hills_energy; - delete hills_energy_gradients; - hills_energy = hills_energy_backup; - hills_energy_gradients = hills_energy_gradients_backup; - is.setstate(std::ios::failbit); - return is; - } - } - } - - if (cvm::debug()) - cvm::log("Successfully read new grids for bias \""+ - this->name+"\""+ - ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+"\n"); - - cvm::log(" read biasing energy and forces from grids.\n"); - - if (hills_energy_backup != NULL) { - // now that we have successfully updated the grids, delete the - // backup copies - if (cvm::debug()) - cvm::log("Deallocating the older grids.\n"); - - delete hills_energy_backup; - delete hills_energy_gradients_backup; } } - // Save references to the end of the list of existing hills, so that it can - // be cleared if hills are read successfully state + // Save references to the end of the list of existing hills, so that they can + // be cleared if hills are read successfully from the stream bool const existing_hills = !hills.empty(); size_t const old_hills_size = hills.size(); hill_iter old_hills_end = hills.end(); @@ -1429,17 +1380,20 @@ std::istream & colvarbias_meta::read_state_data(std::istream& is) while (read_hill(is)) { if (cvm::debug()) { cvm::log("Read a previously saved hill under the " - "metadynamics bias \""+ - this->name+"\", created at step "+ - cvm::to_str((hills.back()).it)+".\n"); + "metadynamics bias \"" + + this->name + "\", created at step " + cvm::to_str((hills.back()).it) + + "; position in stream is " + cvm::to_str(is.tellg()) + ".\n"); } } + is.clear(); + new_hills_begin = hills.end(); - cvm::log(" read "+cvm::to_str(hills.size() - old_hills_size)+ - " additional explicit hills.\n"); + cvm::log(" successfully read "+cvm::to_str(hills.size() - old_hills_size)+ + " explicit hills from state.\n"); if (existing_hills) { + // Prune any hills that pre-existed those just read hills.erase(hills.begin(), old_hills_end); hills_off_grid.erase(hills_off_grid.begin(), old_hills_off_grid_end); if (cvm::debug()) { @@ -1448,6 +1402,46 @@ std::istream & colvarbias_meta::read_state_data(std::istream& is) } } + // If rebinGrids is set, rebin the grids based on the current information + rebin_grids_after_restart(); + + if (use_grids) { + if (!hills_off_grid.empty()) { + cvm::log(cvm::to_str(hills_off_grid.size())+" hills are near the " + "grid boundaries: they will be computed analytically " + "and saved to the state files.\n"); + } + } + + colvarbias_ti::read_state_data(is); + + if (cvm::debug()) + cvm::log("colvarbias_meta::read_restart() done\n"); + + has_data = true; + + if (comm == multiple_replicas) { + read_replica_files(); + } + + return is; +} + + +std::istream & colvarbias_meta::read_state_data(std::istream& is) +{ + return read_state_data_template_(is); +} + + +cvm::memory_stream &colvarbias_meta::read_state_data(cvm::memory_stream &is) +{ + return read_state_data_template_(is); +} + + +void colvarbias_meta::rebin_grids_after_restart() +{ if (rebin_grids) { // allocate new grids (based on the new boundaries and widths just @@ -1462,9 +1456,9 @@ std::istream & colvarbias_meta::read_state_data(std::istream& is) if (cvm::debug()) { std::ostringstream tmp_os; tmp_os << "hills_energy parameters:\n"; - hills_energy->write_params(tmp_os); + tmp_os << hills_energy->get_state_params(); tmp_os << "new_hills_energy parameters:\n"; - new_hills_energy->write_params(tmp_os); + tmp_os << new_hills_energy->get_state_params(); cvm::log(tmp_os.str()); } @@ -1494,116 +1488,184 @@ std::istream & colvarbias_meta::read_state_data(std::istream& is) if (!hills.empty()) recount_hills_off_grid(hills.begin(), hills.end(), hills_energy); } +} - if (use_grids) { - if (!hills_off_grid.empty()) { - cvm::log(cvm::to_str(hills_off_grid.size())+" hills are near the " - "grid boundaries: they will be computed analytically " - "and saved to the state files.\n"); - } + +template +OST &colvarbias_meta::write_hill_template_(OST &os, colvarbias_meta::hill const &h) +{ + bool const formatted = !std::is_same::value; + + if (formatted) { + os.setf(std::ios::scientific, std::ios::floatfield); } - colvarbias_ti::read_state_data(is); + write_state_data_key(os, "hill", false); - if (cvm::debug()) - cvm::log("colvarbias_meta::read_restart() done\n"); + if (formatted) + os << "{\n"; - has_data = true; + write_state_data_key(os, "step", false); + if (formatted) + os << std::setw(cvm::it_width); + os << h.it; + if (formatted) + os << "\n"; - if (comm != single_replica) { - read_replica_files(); + write_state_data_key(os, "weight", false); + if (formatted) + os << std::setprecision(cvm::en_prec) << std::setw(cvm::en_width); + os << h.W; + if (formatted) + os << "\n"; + + size_t i; + write_state_data_key(os, "centers", false); + for (i = 0; i < (h.centers).size(); i++) { + if (formatted) + os << " " << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width); + os << h.centers[i]; } + if (formatted) + os << "\n"; - return is; + // For backward compatibility, write the widths instead of the sigmas + write_state_data_key(os, "widths", false); + for (i = 0; i < (h.sigmas).size(); i++) { + if (formatted) + os << " " << std::setprecision(cvm::cv_prec) << std::setw(cvm::cv_width); + os << 2.0 * h.sigmas[i]; + } + if (formatted) + os << "\n"; + + if (h.replica.size()) { + write_state_data_key(os, "replicaID", false); + os << h.replica; + if (formatted) + os << "\n"; + } + + if (formatted) + os << "}\n"; + + return os; +} + + +std::ostream &colvarbias_meta::write_hill(std::ostream &os, colvarbias_meta::hill const &h) +{ + return write_hill_template_(os, h); } -inline std::istream & reset_istream(std::istream &is, size_t start_pos) +cvm::memory_stream &colvarbias_meta::write_hill(cvm::memory_stream &os, + colvarbias_meta::hill const &h) +{ + return write_hill_template_(os, h); +} + + +template IST &hill_stream_error(IST &is, size_t start_pos, std::string const &key) { is.clear(); - is.seekg(start_pos, std::ios::beg); + is.seekg(start_pos); is.setstate(std::ios::failbit); + cvm::error("Error: in reading data for keyword \"" + key + "\" from stream.\n", + COLVARS_INPUT_ERROR); return is; } -std::istream & colvarbias_meta::read_hill(std::istream &is) +template IST &colvarbias_meta::read_hill_template_(IST &is) { - if (!is) return is; // do nothing if failbit is set + if (!is) + return is; // do nothing if failbit is set - std::streampos const start_pos = is.tellg(); - size_t i = 0; + bool const formatted = !std::is_same::value; + + auto const start_pos = is.tellg(); - std::string data; - if ( !(is >> read_block("hill", &data)) ) { - return reset_istream(is, start_pos); + std::string key; + if (!(is >> key) || (key != "hill")) { + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::failbit); + return is; } - std::istringstream data_is(data); + if (formatted) { + std::string brace; + if (!(is >> brace) || (brace != "{")) { + return hill_stream_error(is, start_pos, "hill"); + } + } cvm::step_number h_it = 0L; cvm::real h_weight = 0.0; std::vector h_centers(num_variables()); - for (i = 0; i < num_variables(); i++) { + for (size_t i = 0; i < num_variables(); i++) { h_centers[i].type(variables(i)->value()); } std::vector h_sigmas(num_variables()); std::string h_replica; - std::string keyword; - while (data_is >> keyword) { + if (!read_state_data_key(is, "step") || !(is >> h_it)) { + return hill_stream_error(is, start_pos, "step"); + } - if (keyword == "step") { - if ( !(data_is >> h_it)) { - return reset_istream(is, start_pos); - } - if ((h_it <= state_file_step) && !restart_keep_hills) { - if (cvm::debug()) - cvm::log("Skipping a hill older than the state file for metadynamics bias \""+ - this->name+"\""+ - ((comm != single_replica) ? ", replica \""+replica_id+"\"" : "")+"\n"); - return is; - } + if (read_state_data_key(is, "weight")) { + if (!(is >> h_weight)) { + return hill_stream_error(is, start_pos, "weight"); } + } - if (keyword == "weight") { - if ( !(data_is >> h_weight)) { - return reset_istream(is, start_pos); + if (read_state_data_key(is, "centers")) { + for (size_t i = 0; i < num_variables(); i++) { + if (!(is >> h_centers[i])) { + return hill_stream_error(is, start_pos, "centers"); } } + } - if (keyword == "centers") { - for (i = 0; i < num_variables(); i++) { - if ( !(data_is >> h_centers[i])) { - return reset_istream(is, start_pos); - } + if (read_state_data_key(is, "widths")) { + for (size_t i = 0; i < num_variables(); i++) { + if (!(is >> h_sigmas[i])) { + return hill_stream_error(is, start_pos, "widths"); } + // For backward compatibility, read the widths instead of the sigmas + h_sigmas[i] /= 2.0; } + } - if (keyword == "widths") { - for (i = 0; i < num_variables(); i++) { - if ( !(data_is >> h_sigmas[i])) { - return reset_istream(is, start_pos); - } - // For backward compatibility, read the widths instead of the sigmas - h_sigmas[i] /= 2.0; + if (comm != single_replica) { + if (read_state_data_key(is, "replicaID")) { + if (!(is >> h_replica)) { + return hill_stream_error(is, start_pos, "replicaID"); + } + if (h_replica != replica_id) { + cvm::error("Error: trying to read a hill created by replica \"" + h_replica + + "\" for replica \"" + replica_id + "\"; did you swap output files?\n", + COLVARS_INPUT_ERROR); + return hill_stream_error(is, start_pos, "replicaID"); } } + } - if (comm != single_replica) { - if (keyword == "replicaID") { - if ( !(data_is >> h_replica)) { - return reset_istream(is, start_pos); - } - if (h_replica != replica_id) { - cvm::error("Error: trying to read a hill created by replica \""+ - h_replica+"\" for replica \""+replica_id+ - "\"; did you swap output files?\n", COLVARS_INPUT_ERROR); - } - } + if (formatted) { + std::string brace; + if (!(is >> brace) || (brace != "}")) { + return hill_stream_error(is, start_pos, "hill"); } } + if ((h_it <= state_file_step) && !restart_keep_hills) { + if (cvm::debug()) + cvm::log("Skipping a hill older than the state file for metadynamics bias \"" + this->name + + "\"" + ((comm != single_replica) ? ", replica \"" + replica_id + "\"" : "") + "\n"); + return is; + } + hill_iter const hills_end = hills.end(); hills.push_back(hill(h_it, h_weight, h_centers, h_sigmas, h_replica)); if (new_hills_begin == hills_end) { @@ -1616,7 +1678,7 @@ std::istream & colvarbias_meta::read_hill(std::istream &is) // add this also to the list of hills that are off-grid, which will // be computed analytically cvm::real const min_dist = - hills_energy->bin_distance_from_boundaries((hills.back()).centers, true); + hills_energy->bin_distance_from_boundaries((hills.back()).centers, true); if (min_dist < (3.0 * cvm::floor(hill_width)) + 1.0) { hills_off_grid.push_back(hills.back()); } @@ -1627,6 +1689,18 @@ std::istream & colvarbias_meta::read_hill(std::istream &is) } +std::istream &colvarbias_meta::read_hill(std::istream &is) +{ + return read_hill_template_(is); +} + + +cvm::memory_stream &colvarbias_meta::read_hill(cvm::memory_stream &is) +{ + return read_hill_template_(is); +} + + int colvarbias_meta::setup_output() { int error_code = COLVARS_OK; @@ -1759,36 +1833,32 @@ std::string const colvarbias_meta::get_state_params() const } -std::ostream & colvarbias_meta::write_state_data(std::ostream& os) +template OST &colvarbias_meta::write_state_data_template_(OST &os) { if (use_grids) { // this is a very good time to project hills, if you haven't done // it already! - project_hills(new_hills_begin, hills.end(), - hills_energy, hills_energy_gradients); + project_hills(new_hills_begin, hills.end(), hills_energy, hills_energy_gradients); new_hills_begin = hills.end(); // write down the grids to the restart file - os << " hills_energy\n"; + write_state_data_key(os, "hills_energy"); hills_energy->write_restart(os); - os << " hills_energy_gradients\n"; + write_state_data_key(os, "hills_energy_gradients"); hills_energy_gradients->write_restart(os); } - if ( (!use_grids) || keep_hills ) { + if ((!use_grids) || keep_hills) { // write all hills currently in memory - for (std::list::const_iterator h = this->hills.begin(); - h != this->hills.end(); - h++) { - os << *h; + for (std::list::const_iterator h = this->hills.begin(); h != this->hills.end(); h++) { + write_hill(os, *h); } } else { // write just those that are near the grid boundaries for (std::list::const_iterator h = this->hills_off_grid.begin(); - h != this->hills_off_grid.end(); - h++) { - os << *h; + h != this->hills_off_grid.end(); h++) { + write_hill(os, *h); } } @@ -1797,6 +1867,18 @@ std::ostream & colvarbias_meta::write_state_data(std::ostream& os) } +std::ostream & colvarbias_meta::write_state_data(std::ostream& os) +{ + return write_state_data_template_(os); +} + + +cvm::memory_stream &colvarbias_meta::write_state_data(cvm::memory_stream &os) +{ + return write_state_data_template_(os); +} + + int colvarbias_meta::write_state_to_replicas() { int error_code = COLVARS_OK; @@ -2048,43 +2130,3 @@ colvarbias_meta::hill::operator = (colvarbias_meta::hill const &h) colvarbias_meta::hill::~hill() {} - - -std::ostream & operator << (std::ostream &os, colvarbias_meta::hill const &h) -{ - os.setf(std::ios::scientific, std::ios::floatfield); - - os << "hill {\n"; - os << " step " << std::setw(cvm::it_width) << h.it << "\n"; - os << " weight " - << std::setprecision(cvm::en_prec) - << std::setw(cvm::en_width) - << h.W << "\n"; - - if (h.replica.size()) - os << " replicaID " << h.replica << "\n"; - - size_t i; - os << " centers "; - for (i = 0; i < (h.centers).size(); i++) { - os << " " - << std::setprecision(cvm::cv_prec) - << std::setw(cvm::cv_width) - << h.centers[i]; - } - os << "\n"; - - // For backward compatibility, write the widths instead of the sigmas - os << " widths "; - for (i = 0; i < (h.sigmas).size(); i++) { - os << " " - << std::setprecision(cvm::cv_prec) - << std::setw(cvm::cv_width) - << 2.0 * h.sigmas[i]; - } - os << "\n"; - - os << "}\n"; - - return os; -} diff --git a/src/colvarbias_meta.h b/src/colvarbias_meta.h index 59ce6a83f..f85bb0fdc 100644 --- a/src/colvarbias_meta.h +++ b/src/colvarbias_meta.h @@ -17,6 +17,7 @@ #include "colvarbias.h" #include "colvargrid.h" + /// Metadynamics bias (implementation of \link colvarbias \endlink) class colvarbias_meta : public virtual colvarbias, @@ -57,8 +58,25 @@ class colvarbias_meta virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &state_conf); - virtual std::ostream & write_state_data(std::ostream &os); - virtual std::istream & read_state_data(std::istream &os); + + virtual std::ostream &write_state_data(std::ostream &os); + virtual cvm::memory_stream &write_state_data(cvm::memory_stream &os); + virtual std::istream &read_state_data(std::istream &is); + virtual cvm::memory_stream &read_state_data(cvm::memory_stream &is); + +private: + + template + IST &read_grid_data_template_(IST &is, std::string const &key, GT *grid, GT *backup_grid); + + template IST &read_state_data_template_(IST &is); + + template OST &write_state_data_template_(OST &os); + +public: + + /// Function called by read_state_data() to execute rebinning (if requested) + void rebin_grids_after_restart(); virtual int setup_output(); virtual int write_output_files(); @@ -105,11 +123,24 @@ class colvarbias_meta /// Regenerate the hills_off_grid list void recount_hills_off_grid(hill_iter h_first, hill_iter h_last, - colvar_grid_scalar *ge); + colvar_grid_scalar *ge); + + template OST &write_hill_template_(OST &os, colvarbias_meta::hill const &h); + + /// Write a hill to a formatted stream + std::ostream &write_hill(std::ostream &os, hill const &h); + + /// Write a hill to an unformatted stream + cvm::memory_stream &write_hill(cvm::memory_stream &os, hill const &h); - /// Read a hill from a file + template IST &read_hill_template_(IST &is); + + /// Read a new hill from a formatted stream std::istream & read_hill(std::istream &is); + /// Read a new hill from an unformatted stream + cvm::memory_stream & read_hill(cvm::memory_stream &is); + /// \brief Add a new hill; if a .hills trajectory is written, /// write it there; if there is more than one replica, communicate /// it to the others @@ -401,9 +432,6 @@ class colvarbias_meta::hill { /// Represent the hill ina string suitable for a trajectory file std::string output_traj(); - /// Write the hill to an output stream - friend std::ostream & operator << (std::ostream &os, hill const &h); - }; diff --git a/src/colvarbias_restraint.cpp b/src/colvarbias_restraint.cpp index 0a66a2999..56200198d 100644 --- a/src/colvarbias_restraint.cpp +++ b/src/colvarbias_restraint.cpp @@ -844,18 +844,6 @@ int colvarbias_restraint_harmonic::set_state_params(std::string const &conf) } -std::ostream & colvarbias_restraint_harmonic::write_state_data(std::ostream &os) -{ - return colvarbias_ti::write_state_data(os); -} - - -std::istream & colvarbias_restraint_harmonic::read_state_data(std::istream &is) -{ - return colvarbias_ti::read_state_data(is); -} - - std::ostream & colvarbias_restraint_harmonic::write_traj_label(std::ostream &os) { colvarbias_restraint::write_traj_label(os); @@ -1143,18 +1131,6 @@ int colvarbias_restraint_harmonic_walls::set_state_params(std::string const &con } -std::ostream & colvarbias_restraint_harmonic_walls::write_state_data(std::ostream &os) -{ - return colvarbias_ti::write_state_data(os); -} - - -std::istream & colvarbias_restraint_harmonic_walls::read_state_data(std::istream &is) -{ - return colvarbias_ti::read_state_data(is); -} - - std::ostream & colvarbias_restraint_harmonic_walls::write_traj_label(std::ostream &os) { colvarbias_restraint::write_traj_label(os); @@ -1300,18 +1276,6 @@ int colvarbias_restraint_linear::set_state_params(std::string const &conf) } -std::ostream & colvarbias_restraint_linear::write_state_data(std::ostream &os) -{ - return colvarbias_ti::write_state_data(os); -} - - -std::istream & colvarbias_restraint_linear::read_state_data(std::istream &is) -{ - return colvarbias_ti::read_state_data(is); -} - - std::ostream & colvarbias_restraint_linear::write_traj_label(std::ostream &os) { colvarbias_restraint::write_traj_label(os); diff --git a/src/colvarbias_restraint.h b/src/colvarbias_restraint.h index 45a96d14f..f030a5cad 100644 --- a/src/colvarbias_restraint.h +++ b/src/colvarbias_restraint.h @@ -38,9 +38,6 @@ class colvarbias_restraint virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &conf); - // virtual std::ostream & write_state_data(std::ostream &os); - // virtual std::istream & read_state_data(std::istream &os); - virtual std::ostream & write_traj_label(std::ostream &os); virtual std::ostream & write_traj(std::ostream &os); @@ -242,8 +239,6 @@ class colvarbias_restraint_harmonic virtual int update(); virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &conf); - virtual std::ostream & write_state_data(std::ostream &os); - virtual std::istream & read_state_data(std::istream &os); virtual std::ostream & write_traj_label(std::ostream &os); virtual std::ostream & write_traj(std::ostream &os); virtual int change_configuration(std::string const &conf); @@ -269,8 +264,6 @@ class colvarbias_restraint_harmonic_walls virtual int update(); virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &conf); - virtual std::ostream & write_state_data(std::ostream &os); - virtual std::istream & read_state_data(std::istream &os); virtual std::ostream & write_traj_label(std::ostream &os); virtual std::ostream & write_traj(std::ostream &os); @@ -311,8 +304,6 @@ class colvarbias_restraint_linear virtual std::string const get_state_params() const; virtual int set_state_params(std::string const &conf); - virtual std::ostream & write_state_data(std::ostream &os); - virtual std::istream & read_state_data(std::istream &os); virtual std::ostream & write_traj_label(std::ostream &os); virtual std::ostream & write_traj(std::ostream &os); diff --git a/src/colvarcomp_apath.cpp b/src/colvarcomp_apath.cpp index a5535ba40..38c570add 100644 --- a/src/colvarcomp_apath.cpp +++ b/src/colvarcomp_apath.cpp @@ -7,11 +7,12 @@ // If you wish to distribute your changes, please submit them to the // Colvars repository at GitHub. -#include #include #include #include +#include #include +#include #include "colvarvalue.h" #include "colvarparse.h" diff --git a/src/colvargrid.cpp b/src/colvargrid.cpp index 2fd385cce..5ab10439c 100644 --- a/src/colvargrid.cpp +++ b/src/colvargrid.cpp @@ -37,6 +37,58 @@ colvar_grid_count::colvar_grid_count(std::vector &colvars, : colvar_grid(colvars, def_count, 1, margin) {} +std::string colvar_grid_count::get_state_params() const +{ + return colvar_grid::get_state_params(); +} + +int colvar_grid_count::parse_params(std::string const &conf, + colvarparse::Parse_Mode const parse_mode) +{ + return colvar_grid::parse_params(conf, parse_mode); +} + +std::istream & colvar_grid_count::read_restart(std::istream &is) +{ + return colvar_grid::read_restart(is); +} + +cvm::memory_stream & colvar_grid_count::read_restart(cvm::memory_stream &is) +{ + return colvar_grid::read_restart(is); +} + +std::ostream & colvar_grid_count::write_restart(std::ostream &os) +{ + return colvar_grid::write_restart(os); +} + +cvm::memory_stream & colvar_grid_count::write_restart(cvm::memory_stream &os) +{ + return colvar_grid::write_restart(os); +} + +std::istream &colvar_grid_count::read_raw(std::istream &is) +{ + return colvar_grid::read_raw(is); +} + +cvm::memory_stream &colvar_grid_count::read_raw(cvm::memory_stream &is) +{ + return colvar_grid::read_raw(is); +} + +std::ostream &colvar_grid_count::write_raw(std::ostream &os, size_t const buf_size) const +{ + return colvar_grid::write_raw(os, buf_size); +} + +cvm::memory_stream &colvar_grid_count::write_raw(cvm::memory_stream &os, + size_t const buf_size) const +{ + return colvar_grid::write_raw(os, buf_size); +} + std::istream & colvar_grid_count::read_multicol(std::istream &is, bool add) { return colvar_grid::read_multicol(is, add); @@ -96,6 +148,58 @@ colvar_grid_scalar::~colvar_grid_scalar() { } +std::string colvar_grid_scalar::get_state_params() const +{ + return colvar_grid::get_state_params(); +} + +int colvar_grid_scalar::parse_params(std::string const &conf, + colvarparse::Parse_Mode const parse_mode) +{ + return colvar_grid::parse_params(conf, parse_mode); +} + +std::istream &colvar_grid_scalar::read_restart(std::istream &is) +{ + return colvar_grid::read_restart(is); +} + +cvm::memory_stream &colvar_grid_scalar::read_restart(cvm::memory_stream &is) +{ + return colvar_grid::read_restart(is); +} + +std::ostream &colvar_grid_scalar::write_restart(std::ostream &os) +{ + return colvar_grid::write_restart(os); +} + +cvm::memory_stream &colvar_grid_scalar::write_restart(cvm::memory_stream &os) +{ + return colvar_grid::write_restart(os); +} + +std::istream &colvar_grid_scalar::read_raw(std::istream &is) +{ + return colvar_grid::read_raw(is); +} + +cvm::memory_stream &colvar_grid_scalar::read_raw(cvm::memory_stream &is) +{ + return colvar_grid::read_raw(is); +} + +std::ostream &colvar_grid_scalar::write_raw(std::ostream &os, size_t const buf_size) const +{ + return colvar_grid::write_raw(os, buf_size); +} + +cvm::memory_stream &colvar_grid_scalar::write_raw(cvm::memory_stream &os, + size_t const buf_size) const +{ + return colvar_grid::write_raw(os, buf_size); +} + std::istream & colvar_grid_scalar::read_multicol(std::istream &is, bool add) { return colvar_grid::read_multicol(is, add); @@ -280,6 +384,57 @@ colvar_grid_gradient::colvar_grid_gradient(std::string &filename) cvm::main()->proxy->close_input_stream(filename); } +std::string colvar_grid_gradient::get_state_params() const +{ + return colvar_grid::get_state_params(); +} + +int colvar_grid_gradient::parse_params(std::string const &conf, + colvarparse::Parse_Mode const parse_mode) +{ + return colvar_grid::parse_params(conf, parse_mode); +} + +std::istream &colvar_grid_gradient::read_restart(std::istream &is) +{ + return colvar_grid::read_restart(is); +} + +cvm::memory_stream &colvar_grid_gradient::read_restart(cvm::memory_stream &is) +{ + return colvar_grid::read_restart(is); +} + +std::ostream &colvar_grid_gradient::write_restart(std::ostream &os) +{ + return colvar_grid::write_restart(os); +} + +cvm::memory_stream &colvar_grid_gradient::write_restart(cvm::memory_stream &os) +{ + return colvar_grid::write_restart(os); +} + +std::istream &colvar_grid_gradient::read_raw(std::istream &is) +{ + return colvar_grid::read_raw(is); +} + +cvm::memory_stream &colvar_grid_gradient::read_raw(cvm::memory_stream &is) +{ + return colvar_grid::read_raw(is); +} + +std::ostream &colvar_grid_gradient::write_raw(std::ostream &os, size_t const buf_size) const +{ + return colvar_grid::write_raw(os, buf_size); +} + +cvm::memory_stream &colvar_grid_gradient::write_raw(cvm::memory_stream &os, + size_t const buf_size) const +{ + return colvar_grid::write_raw(os, buf_size); +} std::istream & colvar_grid_gradient::read_multicol(std::istream &is, bool add) { diff --git a/src/colvargrid.h b/src/colvargrid.h index e2fc1e0fe..ef3d96734 100644 --- a/src/colvargrid.h +++ b/src/colvargrid.h @@ -10,8 +10,7 @@ #ifndef COLVARGRID_H #define COLVARGRID_H -#include -#include +#include #include "colvar.h" #include "colvarmodule.h" @@ -24,7 +23,8 @@ /// Only scalar colvars supported so far: vector colvars are treated as arrays template class colvar_grid : public colvarparse { -protected: + //protected: +public: // TODO create accessors for these after all instantiations work /// Number of dimensions size_t nd; @@ -803,109 +803,12 @@ template class colvar_grid : public colvarparse { } } - /// Write the grid parameters (number of colvars, boundaries, width and number of points) - std::ostream & write_params(std::ostream &os) - { - size_t i; - os << "grid_parameters {\n n_colvars " << nd << "\n"; - - os << " lower_boundaries "; - for (i = 0; i < nd; i++) - os << " " << lower_boundaries[i]; - os << "\n"; - - os << " upper_boundaries "; - for (i = 0; i < nd; i++) - os << " " << upper_boundaries[i]; - os << "\n"; - - os << " widths "; - for (i = 0; i < nd; i++) - os << " " << widths[i]; - os << "\n"; - - os << " sizes "; - for (i = 0; i < nd; i++) - os << " " << nx[i]; - os << "\n"; + /// Write the current grid parameters to a string + std::string get_state_params() const; - os << "}\n"; - return os; - } - - /// Read a grid definition from a config string + /// Read new grid parameters from a string int parse_params(std::string const &conf, - colvarparse::Parse_Mode const parse_mode = colvarparse::parse_normal) - { - if (cvm::debug()) cvm::log("Reading grid configuration from string.\n"); - - std::vector old_nx = nx; - std::vector old_lb = lower_boundaries; - std::vector old_ub = upper_boundaries; - std::vector old_w = widths; - - { - size_t nd_in = 0; - // this is only used in state files - colvarparse::get_keyval(conf, "n_colvars", nd_in, nd, colvarparse::parse_silent); - if (nd_in != nd) { - cvm::error("Error: trying to read data for a grid " - "that contains a different number of colvars ("+ - cvm::to_str(nd_in)+") than the grid defined " - "in the configuration file("+cvm::to_str(nd)+ - ").\n"); - return COLVARS_ERROR; - } - } - - // underscore keywords are used in state file - colvarparse::get_keyval(conf, "lower_boundaries", - lower_boundaries, lower_boundaries, colvarparse::parse_silent); - colvarparse::get_keyval(conf, "upper_boundaries", - upper_boundaries, upper_boundaries, colvarparse::parse_silent); - - // camel case keywords are used in config file - colvarparse::get_keyval(conf, "lowerBoundaries", - lower_boundaries, lower_boundaries, parse_mode); - colvarparse::get_keyval(conf, "upperBoundaries", - upper_boundaries, upper_boundaries, parse_mode); - - colvarparse::get_keyval(conf, "widths", widths, widths, parse_mode); - - // only used in state file - colvarparse::get_keyval(conf, "sizes", nx, nx, colvarparse::parse_silent); - - if (nd < lower_boundaries.size()) nd = lower_boundaries.size(); - - if (! use_actual_value.size()) use_actual_value.assign(nd, false); - if (! periodic.size()) periodic.assign(nd, false); - if (! widths.size()) widths.assign(nd, 1.0); - - cvm::real eps = 1.e-10; - - bool new_params = false; - if (old_nx.size()) { - for (size_t i = 0; i < nd; i++) { - if (old_nx[i] != nx[i] || - cvm::sqrt(cv[i]->dist2(old_lb[i], lower_boundaries[i])) > eps || - cvm::sqrt(cv[i]->dist2(old_ub[i], upper_boundaries[i])) > eps || - cvm::fabs(old_w[i] - widths[i]) > eps) { - new_params = true; - } - } - } else { - new_params = true; - } - - // reallocate the array in case the grid params have just changed - if (new_params) { - init_from_boundaries(); - // data.clear(); // no longer needed: setup calls clear() - return this->setup(nx, T(), mult); - } - - return COLVARS_OK; - } + colvarparse::Parse_Mode const parse_mode = colvarparse::parse_normal); /// \brief Check that the grid information inside (boundaries, /// widths, ...) is consistent with the current setting of the @@ -950,83 +853,33 @@ template class colvar_grid : public colvarparse { } } + /// Read all grid parameters and data from a formatted stream + std::istream & read_restart(std::istream &is); - /// \brief Read grid entry in restart file - std::istream & read_restart(std::istream &is) - { - std::streampos const start_pos = is.tellg(); - std::string key, conf; - if ((is >> key) && (key == std::string("grid_parameters"))) { - is.seekg(start_pos, std::ios::beg); - is >> colvarparse::read_block("grid_parameters", &conf); - parse_params(conf, colvarparse::parse_silent); - } else { - cvm::log("Grid parameters are missing in the restart file, using those from the configuration.\n"); - is.seekg(start_pos, std::ios::beg); - } - read_raw(is); - return is; - } - - /// \brief Write grid entry in restart file - std::ostream & write_restart(std::ostream &os) - { - write_params(os); - write_raw(os); - return os; - } - + /// Read all grid parameters and data from an unformatted stream + cvm::memory_stream & read_restart(cvm::memory_stream &is); - /// \brief Write the grid data without labels, as they are - /// represented in memory - /// \param buf_size Number of values per line - std::ostream & write_raw(std::ostream &os, - size_t const buf_size = 3) const - { - std::streamsize const w = os.width(); - std::streamsize const p = os.precision(); + /// Write all grid parameters and data to a formatted stream + std::ostream & write_restart(std::ostream &os); - std::vector ix = new_index(); - size_t count = 0; - for ( ; index_ok(ix); incr(ix)) { - for (size_t imult = 0; imult < mult; imult++) { - os << " " - << std::setw(w) << std::setprecision(p) - << value_output(ix, imult); - if (((++count) % buf_size) == 0) - os << "\n"; - } - } - // write a final newline only if buffer is not empty - if ((count % buf_size) != 0) - os << "\n"; + /// Write all grid parameters and data to an unformatted stream + cvm::memory_stream & write_restart(cvm::memory_stream &os); - return os; - } + /// Read all grid parameters and data from a formatted stream + std::istream &read_raw(std::istream &is); - /// \brief Read data written by colvar_grid::write_raw() - std::istream & read_raw(std::istream &is) - { - std::streampos const start_pos = is.tellg(); + /// Read all grid parameters and data from an unformatted stream + cvm::memory_stream &read_raw(cvm::memory_stream &is); - for (std::vector ix = new_index(); index_ok(ix); incr(ix)) { - for (size_t imult = 0; imult < mult; imult++) { - T new_value; - if (is >> new_value) { - value_input(ix, new_value, imult); - } else { - is.clear(); - is.seekg(start_pos, std::ios::beg); - is.setstate(std::ios::failbit); - cvm::error("Error: failed to read all of the grid points from file. Possible explanations: grid parameters in the configuration (lowerBoundary, upperBoundary, width) are different from those in the file, or the file is corrupt/incomplete.\n"); - return is; - } - } - } + /// Write all grid data to a formatted stream (without labels, as they are represented in memory) + /// \param[in,out] os Stream object + /// \param[in] buf_size Number of values per line + std::ostream &write_raw(std::ostream &os, size_t const buf_size = 3) const; - has_data = true; - return is; - } + /// Write all grid data to an unformatted stream + /// \param[in,out] os Stream object + /// \param[in] buf_size Number of values per line (note: ignored because there is no formatting) + cvm::memory_stream &write_raw(cvm::memory_stream &os, size_t const buf_size = 3) const; /// Read a grid written by write_multicol(), incrementing if add is true std::istream & read_multicol(std::istream &is, bool add = false); @@ -1088,6 +941,41 @@ class colvar_grid_count : public colvar_grid return new_data[address(ix) + imult]; } + /// Write the current grid parameters to a string + std::string get_state_params() const; + + /// Read new grid parameters from a string + int parse_params(std::string const &conf, + colvarparse::Parse_Mode const parse_mode = colvarparse::parse_normal); + + /// Read all grid parameters and data from a formatted stream + std::istream & read_restart(std::istream &is); + + /// Read all grid parameters and data from an unformatted stream + cvm::memory_stream & read_restart(cvm::memory_stream &is); + + /// Write all grid parameters and data to a formatted stream + std::ostream & write_restart(std::ostream &os); + + /// Write all grid parameters and data to an unformatted stream + cvm::memory_stream & write_restart(cvm::memory_stream &os); + + /// Read all grid parameters and data from a formatted stream + std::istream &read_raw(std::istream &is); + + /// Read all grid parameters and data from an unformatted stream + cvm::memory_stream &read_raw(cvm::memory_stream &is); + + /// Write all grid data to a formatted stream (without labels, as they are represented in memory) + /// \param[in,out] os Stream object + /// \param[in] buf_size Number of values per line + std::ostream &write_raw(std::ostream &os, size_t const buf_size = 3) const; + + /// Write all grid data to an unformatted stream + /// \param[in,out] os Stream object + /// \param[in] buf_size Number of values per line (note: ignored because there is no formatting) + cvm::memory_stream &write_raw(cvm::memory_stream &os, size_t const buf_size = 3) const; + /// Read a grid written by write_multicol(), incrementin if data is true std::istream & read_multicol(std::istream &is, bool add = false); @@ -1261,6 +1149,41 @@ class colvar_grid_scalar : public colvar_grid has_data = true; } + /// Write the current grid parameters to a string + std::string get_state_params() const; + + /// Read new grid parameters from a string + int parse_params(std::string const &conf, + colvarparse::Parse_Mode const parse_mode = colvarparse::parse_normal); + + /// Read all grid parameters and data from a formatted stream + std::istream & read_restart(std::istream &is); + + /// Read all grid parameters and data from an unformatted stream + cvm::memory_stream & read_restart(cvm::memory_stream &is); + + /// Write all grid parameters and data to a formatted stream + std::ostream & write_restart(std::ostream &os); + + /// Write all grid parameters and data to an unformatted stream + cvm::memory_stream & write_restart(cvm::memory_stream &os); + + /// Read all grid parameters and data from a formatted stream + std::istream &read_raw(std::istream &is); + + /// Read all grid parameters and data from an unformatted stream + cvm::memory_stream &read_raw(cvm::memory_stream &is); + + /// Write all grid data to a formatted stream (without labels, as they are represented in memory) + /// \param[in,out] os Stream object + /// \param[in] buf_size Number of values per line + std::ostream &write_raw(std::ostream &os, size_t const buf_size = 3) const; + + /// Write all grid data to an unformatted stream + /// \param[in,out] os Stream object + /// \param[in] buf_size Number of values per line (note: ignored because there is no formatting) + cvm::memory_stream &write_raw(cvm::memory_stream &os, size_t const buf_size = 3) const; + /// Read a grid written by write_multicol(), incrementin if data is true std::istream & read_multicol(std::istream &is, bool add = false); @@ -1471,6 +1394,41 @@ class colvar_grid_gradient : public colvar_grid /// Constructor from a multicol file colvar_grid_gradient(std::string &filename); + /// Write the current grid parameters to a string + std::string get_state_params() const; + + /// Read new grid parameters from a string + int parse_params(std::string const &conf, + colvarparse::Parse_Mode const parse_mode = colvarparse::parse_normal); + + /// Read all grid parameters and data from a formatted stream + std::istream & read_restart(std::istream &is); + + /// Read all grid parameters and data from an unformatted stream + cvm::memory_stream & read_restart(cvm::memory_stream &is); + + /// Write all grid parameters and data to a formatted stream + std::ostream & write_restart(std::ostream &os); + + /// Write all grid parameters and data to an unformatted stream + cvm::memory_stream & write_restart(cvm::memory_stream &os); + + /// Read all grid parameters and data from a formatted stream + std::istream &read_raw(std::istream &is); + + /// Read all grid parameters and data from an unformatted stream + cvm::memory_stream &read_raw(cvm::memory_stream &is); + + /// Write all grid data to a formatted stream (without labels, as they are represented in memory) + /// \param[in,out] os Stream object + /// \param[in] buf_size Number of values per line + std::ostream &write_raw(std::ostream &os, size_t const buf_size = 3) const; + + /// Write all grid data to an unformatted stream + /// \param[in,out] os Stream object + /// \param[in] buf_size Number of values per line (note: ignored because there is no formatting) + cvm::memory_stream &write_raw(cvm::memory_stream &os, size_t const buf_size = 3) const; + /// Read a grid written by write_multicol(), incrementin if data is true virtual std::istream & read_multicol(std::istream &is, bool add = false); diff --git a/src/colvargrid_def.h b/src/colvargrid_def.h index f2245f3d8..92861f43b 100644 --- a/src/colvargrid_def.h +++ b/src/colvargrid_def.h @@ -19,6 +19,233 @@ #include "colvarproxy.h" #include "colvar.h" #include "colvargrid.h" +#include "colvars_memstream.h" + + +template IST &read_restart_template_(colvar_grid &g, IST &is) +{ + auto const start_pos = is.tellg(); + std::string conf; + if ((is >> colvarparse::read_block("grid_parameters", &conf)) && + (g.parse_params(conf, colvarparse::parse_restart) == COLVARS_OK) && g.read_raw(is)) { + return is; + } + auto const error_pos = is.tellg(); + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::failbit); + cvm::error("Error: in reading grid state from stream at position " + cvm::to_str(error_pos) + + "\n", + COLVARS_INPUT_ERROR); + return is; +} + + +template std::istream &colvar_grid::read_restart(std::istream &is) +{ + return read_restart_template_(*this, is); +} + + +template cvm::memory_stream &colvar_grid::read_restart(cvm::memory_stream &is) +{ + return read_restart_template_(*this, is); +} + + +template std::ostream &colvar_grid::write_restart(std::ostream &os) +{ + os << "grid_parameters {\n" << get_state_params() << "}\n"; + write_raw(os); + return os; +} + + +template cvm::memory_stream &colvar_grid::write_restart(cvm::memory_stream &os) +{ + os << std::string("grid_parameters") << get_state_params(); + write_raw(os); + return os; +} + + +template IST &read_raw_template_(colvar_grid &g, IST &is) +{ + auto const start_pos = is.tellg(); + + for (std::vector ix = g.new_index(); g.index_ok(ix); g.incr(ix)) { + for (size_t imult = 0; imult < g.mult; imult++) { + T new_value; + if (is >> new_value) { + g.value_input(ix, new_value, imult); + } else { + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::failbit); + cvm::error( + "Error: failed to read all of the grid points from file. Possible explanations: grid " + "parameters in the configuration (lowerBoundary, upperBoundary, width) are different " + "from those in the file, or the file is corrupt/incomplete.\n", + COLVARS_INPUT_ERROR); + return is; + } + } + } + + g.has_data = true; + return is; +} + + +template std::istream &colvar_grid::read_raw(std::istream &is) +{ + return read_raw_template_(*this, is); +} + + +template cvm::memory_stream &colvar_grid::read_raw(cvm::memory_stream &is) +{ + return read_raw_template_(*this, is); +} + + +template +std::ostream &colvar_grid::write_raw(std::ostream &os, size_t const buf_size) const +{ + auto const w = os.width(); + auto const p = os.precision(); + + size_t count = 0; + for (auto ix = new_index(); index_ok(ix); incr(ix)) { + for (size_t imult = 0; imult < mult; imult++) { + os << " " << std::setw(w) << std::setprecision(p) << value_output(ix, imult); + if (((++count) % buf_size) == 0) + os << "\n"; + } + } + // write a final newline only if buffer is not empty + if ((count % buf_size) != 0) + os << "\n"; + + return os; +} + + +template +cvm::memory_stream &colvar_grid::write_raw(cvm::memory_stream &os, size_t const buf_size) const +{ + for (auto ix = new_index(); index_ok(ix); incr(ix)) { + for (size_t imult = 0; imult < mult; imult++) { + os << value_output(ix, imult); + } + } + return os; +} + + +template std::string colvar_grid::get_state_params() const +{ + std::ostringstream os; + size_t i; + os << " n_colvars " << nd << "\n"; + + os << " lower_boundaries "; + for (i = 0; i < nd; i++) + os << " " << lower_boundaries[i]; + os << "\n"; + + os << " upper_boundaries "; + for (i = 0; i < nd; i++) + os << " " << upper_boundaries[i]; + os << "\n"; + + os << " widths "; + for (i = 0; i < nd; i++) + os << " " << widths[i]; + os << "\n"; + + os << " sizes "; + for (i = 0; i < nd; i++) + os << " " << nx[i]; + os << "\n"; + + return os.str(); +} + + +template int colvar_grid::parse_params(std::string const &conf, + colvarparse::Parse_Mode const parse_mode) +{ + if (cvm::debug()) + cvm::log("Reading grid configuration from string.\n"); + + std::vector old_nx = nx; + std::vector old_lb = lower_boundaries; + std::vector old_ub = upper_boundaries; + std::vector old_w = widths; + + { + size_t nd_in = 0; + // this is only used in state files + colvarparse::get_keyval(conf, "n_colvars", nd_in, nd, colvarparse::parse_silent); + if (nd_in != nd) { + cvm::error("Error: trying to read data for a grid " + "that contains a different number of colvars ("+ + cvm::to_str(nd_in)+") than the grid defined " + "in the configuration file("+cvm::to_str(nd)+ + ").\n"); + return COLVARS_ERROR; + } + } + + // underscore keywords are used in state file + colvarparse::get_keyval(conf, "lower_boundaries", + lower_boundaries, lower_boundaries, colvarparse::parse_silent); + colvarparse::get_keyval(conf, "upper_boundaries", + upper_boundaries, upper_boundaries, colvarparse::parse_silent); + + // camel case keywords are used in config file + colvarparse::get_keyval(conf, "lowerBoundaries", + lower_boundaries, lower_boundaries, parse_mode); + colvarparse::get_keyval(conf, "upperBoundaries", + upper_boundaries, upper_boundaries, parse_mode); + + colvarparse::get_keyval(conf, "widths", widths, widths, parse_mode); + + // only used in state file + colvarparse::get_keyval(conf, "sizes", nx, nx, colvarparse::parse_silent); + + if (nd < lower_boundaries.size()) nd = lower_boundaries.size(); + + if (! use_actual_value.size()) use_actual_value.assign(nd, false); + if (! periodic.size()) periodic.assign(nd, false); + if (! widths.size()) widths.assign(nd, 1.0); + + cvm::real eps = 1.e-10; + + bool new_params = false; + if (old_nx.size()) { + for (size_t i = 0; i < nd; i++) { + if (old_nx[i] != nx[i] || + cvm::sqrt(cv[i]->dist2(old_lb[i], lower_boundaries[i])) > eps || + cvm::sqrt(cv[i]->dist2(old_ub[i], upper_boundaries[i])) > eps || + cvm::fabs(old_w[i] - widths[i]) > eps) { + new_params = true; + } + } + } else { + new_params = true; + } + + // reallocate the array in case the grid params have just changed + if (new_params) { + init_from_boundaries(); + // data.clear(); // no longer needed: setup calls clear() + return this->setup(nx, T(), mult); + } + + return COLVARS_OK; +} template diff --git a/src/colvarmodule.cpp b/src/colvarmodule.cpp index 40d75b883..617a74a75 100644 --- a/src/colvarmodule.cpp +++ b/src/colvarmodule.cpp @@ -7,13 +7,14 @@ // If you wish to distribute your changes, please submit them to the // Colvars repository at GitHub. -#include +#include +#include +#include #include +#include +#include #include -#include -#include #include -#include #include "colvarmodule.h" #include "colvarparse.h" @@ -29,7 +30,7 @@ #include "colvarscript.h" #include "colvaratoms.h" #include "colvarcomp.h" - +#include "colvars_memstream.h" /// Track usage of Colvars features @@ -69,6 +70,11 @@ class colvarmodule::usage { }; +namespace { + constexpr uint32_t colvars_magic_number = 2013813594; +} + + colvarmodule::colvarmodule(colvarproxy *proxy_in) { depth_s = 0; @@ -118,6 +124,12 @@ colvarmodule::colvarmodule(colvarproxy *proxy_in) // set initial default values + binary_restart = false; + char const *env_var = getenv("COLVARS_BINARY_RESTART"); + if (env_var && atoi(env_var)) { + binary_restart = true; + } + // "it_restart" will be set by the input state file, if any; // "it" should be updated by the proxy colvarmodule::it = colvarmodule::it_restart = 0; @@ -794,7 +806,9 @@ int colvarmodule::calc() // Write restart file, if different from main output error_code |= write_restart_file(restart_out_name); } else { - error_code |= write_restart_file(output_prefix()+".colvars.state"); + if (output_prefix().size()) { + error_code |= write_restart_file(output_prefix()+".colvars.state"); + } } cvm::increase_depth(); @@ -1079,9 +1093,22 @@ int colvarmodule::write_restart_file(std::string const &out_name) cvm::log("Saving collective variables state to \""+out_name+"\".\n"); std::ostream &restart_out_os = proxy->output_stream(out_name, "state file"); if (!restart_out_os) return COLVARS_FILE_ERROR; - if (!write_restart(restart_out_os)) { - return cvm::error("Error: in writing restart file.\n", COLVARS_FILE_ERROR); + + if (binary_restart) { + cvm::memory_stream mem_os; + if (!write_state(mem_os)) { + return cvm::error("Error: in writing binary state information to file.\n", COLVARS_ERROR); + } + if (!restart_out_os.write(reinterpret_cast(mem_os.output_buffer()), + mem_os.length())) { + return cvm::error("Error: in writing restart file.\n", COLVARS_FILE_ERROR); + } + } else { + if (!write_state(restart_out_os)) { + return cvm::error("Error: in writing restart file.\n", COLVARS_FILE_ERROR); + } } + proxy->close_output_stream(out_name); // Take the opportunity to flush colvars.traj @@ -1094,7 +1121,7 @@ int colvarmodule::write_restart_string(std::string &output) { cvm::log("Saving state to output buffer.\n"); std::ostringstream os; - if (!write_restart(os)) { + if (!write_state(os)) { return cvm::error("Error: in writing restart to buffer.\n", COLVARS_FILE_ERROR); } output = os.str(); @@ -1288,17 +1315,15 @@ int colvarmodule::reset() int colvarmodule::setup_input() { if (proxy->input_prefix().size()) { - // Read a state file - std::string restart_in_name(proxy->input_prefix()+ - std::string(".colvars.state")); - std::istream *input_is = &(proxy->input_stream(restart_in_name, - "restart file/channel", - false)); + + // Read state from a file + + std::string restart_in_name(proxy->input_prefix() + std::string(".colvars.state")); + std::istream *input_is = &(proxy->input_stream(restart_in_name, "restart file/channel", false)); if (!*input_is) { // Try without the suffix ".colvars.state" restart_in_name = proxy->input_prefix(); - input_is = &(proxy->input_stream(restart_in_name, - "restart file/channel")); + input_is = &(proxy->input_stream(restart_in_name, "restart file/channel")); if (!*input_is) { // Error message has already been printed, return now return COLVARS_FILE_ERROR; @@ -1310,22 +1335,72 @@ int colvarmodule::setup_input() proxy->set_input_prefix(""); cvm::log(cvm::line_marker); - cvm::log("Loading state from file \""+restart_in_name+"\".\n"); - read_restart(*input_is); + + input_is->seekg(0, std::ios::end); + size_t const file_size = input_is->tellg(); + input_is->seekg(0, std::ios::beg); + + bool binary_state_file = false; + + uint32_t file_magic_number = 0; + if (file_size > sizeof(uint32_t)) { + if (input_is->read(reinterpret_cast(&file_magic_number), sizeof(uint32_t))) { + if (file_magic_number == colvars_magic_number) { + binary_state_file = true; + } + input_is->seekg(0, std::ios::beg); + } + } + + if (binary_state_file) { + cvm::log("Loading state from binary file \"" + restart_in_name + "\".\n"); + // TODO integrate istream.read() into memory_stream to avoid copying + auto *buf = new unsigned char[file_size]; + if (input_is->read(reinterpret_cast(buf), file_size)) { + cvm::memory_stream mem_is(file_size, buf); + if (!read_state(mem_is)) { + input_is->setstate(std::ios::failbit); + cvm::error("Error: cannot interpret contents of binary file \"" + restart_in_name + + "\".\n", + COLVARS_INPUT_ERROR); + } + } else { + cvm::error("Error: cannot read from binary file \"" + restart_in_name + "\".\n", + COLVARS_INPUT_ERROR); + } + delete[] buf; + } else { + cvm::log("Loading state from text file \"" + restart_in_name + "\".\n"); + read_state(*input_is); + } cvm::log(cvm::line_marker); + // Now that the explicit input file was read, we shall ignore any unformatted buffer + input_state_buffer_.clear(); + proxy->delete_input_stream(restart_in_name); } if (proxy->input_stream_exists("input state string")) { + cvm::log(cvm::line_marker); - cvm::log("Loading state from string.\n"); - read_restart(proxy->input_stream("input state string")); + cvm::log("Loading state from formatted string.\n"); + read_state(proxy->input_stream("input state string")); cvm::log(cvm::line_marker); proxy->delete_input_stream("input state string"); } + if (input_state_buffer_.size() > 0) { + cvm::log(cvm::line_marker); + cvm::log("Loading state from unformatted memory.\n"); + cvm::memory_stream ms(input_state_buffer_.size(), input_state_buffer_.data()); + read_state(ms); + cvm::log(cvm::line_marker); + + input_state_buffer_.clear(); + } + return cvm::get_error(); } @@ -1339,14 +1414,16 @@ int colvarmodule::setup_output() std::string(proxy->restart_output_prefix()+".colvars.state") : std::string(""); + std::string const state_file_format(binary_restart ? " (binary format)" : ""); + if (restart_out_name.size()) { - cvm::log("The restart output state file will be \""+ + cvm::log("The restart output state file" + state_file_format + " will be \""+ restart_out_name+"\".\n"); } output_prefix() = proxy->output_prefix(); if (output_prefix().size()) { - cvm::log("The final output state file will be \""+ + cvm::log("The final output state file" + state_file_format + " will be \""+ (output_prefix().size() ? std::string(output_prefix()+".colvars.state") : std::string("colvars.state"))+"\".\n"); @@ -1385,8 +1462,7 @@ std::string colvarmodule::state_file_prefix(char const *filename) } - -std::istream & colvarmodule::read_restart(std::istream &is) +template IST & colvarmodule::read_state_template_(IST &is) { bool warn_total_forces = false; @@ -1412,8 +1488,11 @@ std::istream & colvarmodule::read_restart(std::istream &is) } if (restart_version() != version()) { - cvm::log("This state file was generated with version "+ - restart_version()+"\n"); + cvm::log("This state file was generated with version " + restart_version() + "\n"); + if (std::is_same::value) { + cvm::log("Warning: compatibility between differetn Colvars versions is not " + "guaranteed for unformatted (binary) state files.\n"); + } } if (restart_version_number() < 20160810) { @@ -1448,35 +1527,73 @@ std::istream & colvarmodule::read_restart(std::istream &is) } +std::istream & colvarmodule::read_state(std::istream &is) +{ + return read_state_template_(is); +} + + +cvm::memory_stream &colvarmodule::read_state(cvm::memory_stream &is) +{ + uint32_t file_magic_number = 0; + if (!(is >> file_magic_number)) { + return is; + } + if (file_magic_number == colvars_magic_number) { + return read_state_template_(is); + } else { + is.setstate(std::ios::failbit); + cvm::error("Error: magic number of binary file (" + + cvm::to_str(static_cast(file_magic_number)) + + ") does not match the expected magic number for a Colvars state file (" + + cvm::to_str(static_cast(colvars_magic_number)) + ").\n", + COLVARS_INPUT_ERROR); + } + return is; +} + + +int colvarmodule::set_input_state_buffer(size_t n, unsigned char *buf) +{ + input_state_buffer_.clear(); + std::copy(buf, buf + n, std::back_inserter(input_state_buffer_)); + return COLVARS_OK; +} + + +int colvarmodule::set_input_state_buffer(std::vector &buf) +{ + input_state_buffer_ = std::move(buf); + return COLVARS_OK; +} + std::istream & colvarmodule::read_objects_state(std::istream &is) { - std::streampos pos = 0; + auto pos = is.tellg(); std::string word; - while (is.good()) { + while (is) { pos = is.tellg(); - word.clear(); - is >> word; - if (word.size()) { + if (is >> word) { - is.seekg(pos, std::ios::beg); + is.seekg(pos); if (word == "colvar") { cvm::increase_depth(); - for (std::vector::iterator cvi = colvars.begin(); - cvi != colvars.end(); - cvi++) { - if ( !((*cvi)->read_state(is)) ) { + for (std::vector::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { + if (!((*cvi)->read_state(is))) { // Here an error signals that the variable is a match, but the // state is corrupt; otherwise, the variable rewinds is silently - cvm::error("Error: in reading restart configuration for " - "collective variable \""+(*cvi)->name+"\".\n", + cvm::error("Error: in reading state for collective variable \"" + + (*cvi)->name + "\" at position " + cvm::to_str(is.tellg()) + + " in stream.\n", COLVARS_INPUT_ERROR); } - if (is.tellg() > pos) break; // found it + if (is.tellg() > pos) + break; // found it } cvm::decrease_depth(); @@ -1493,11 +1610,12 @@ std::istream & colvarmodule::read_objects_state(std::istream &is) } if (!((*bi)->read_state(is))) { // Same as above, an error means a match but the state is incorrect - cvm::error("Error: in reading restart configuration for bias \""+ - (*bi)->name+"\".\n", + cvm::error("Error: in reading state for bias \"" + (*bi)->name + "\" at position " + + cvm::to_str(is.tellg()) + " in stream.\n", COLVARS_INPUT_ERROR); } - if (is.tellg() > pos) break; // found it + if (is.tellg() > pos) + break; // found it } cvm::decrease_depth(); } @@ -1516,6 +1634,25 @@ std::istream & colvarmodule::read_objects_state(std::istream &is) } +cvm::memory_stream &colvarmodule::read_objects_state(cvm::memory_stream &is) +{ + // An unformatted stream must match the objects' exact configuration + cvm::increase_depth(); + for (std::vector::iterator cvi = colvars.begin(); cvi != colvars.end(); cvi++) { + if (!(*cvi)->read_state(is)) { + return is; + } + } + for (std::vector::iterator bi = biases.begin(); bi != biases.end(); bi++) { + if (!(*bi)->read_state(is)) { + return is; + } + } + cvm::decrease_depth(); + return is; +} + + int colvarmodule::print_total_forces_errning(bool warn_total_forces) { if (warn_total_forces) { @@ -1638,18 +1775,24 @@ int colvarmodule::read_traj(char const *traj_filename, } -std::ostream & colvarmodule::write_restart(std::ostream &os) +template OST &colvarmodule::write_state_template_(OST &os) { - os.setf(std::ios::scientific, std::ios::floatfield); - os << "configuration {\n" - << " step " << std::setw(it_width) - << it << "\n" - << " dt " << dt() << "\n" - << " version " << std::string(COLVARS_VERSION) << "\n"; + bool const formatted = !std::is_same::value; + + std::ostringstream oss; + oss.setf(std::ios::scientific, std::ios::floatfield); + oss << " step " << std::setw(it_width) + << it << "\n" + << " dt " << dt() << "\n" + << " version " << std::string(COLVARS_VERSION) << "\n"; if (proxy->units.size() > 0) { - os << " units " << proxy->units << "\n"; + oss << " units " << proxy->units << "\n"; } - os << "}\n\n"; + + os << std::string("configuration"); + if (formatted) os << " {\n"; + os << oss.str(); + if (formatted) os << "}\n\n"; int error_code = COLVARS_OK; @@ -1676,7 +1819,32 @@ std::ostream & colvarmodule::write_restart(std::ostream &os) } -std::ostream & colvarmodule::write_traj_label(std::ostream &os) +std::ostream &colvarmodule::write_state(std::ostream &os) +{ + return write_state_template_(os); +} + + +cvm::memory_stream &colvarmodule::write_state(cvm::memory_stream &os) +{ + if (os << colvars_magic_number) { + write_state_template_(os); + } + return os; +} + + +int colvarmodule::write_state_buffer(std::vector &buffer) +{ + cvm::memory_stream os(buffer); + if (os << colvars_magic_number) { + write_state_template_(os); + } + return os ? COLVARS_OK : COLVARS_ERROR; +} + + +std::ostream &colvarmodule::write_traj_label(std::ostream &os) { os.setf(std::ios::scientific, std::ios::floatfield); @@ -1805,7 +1973,7 @@ void colvarmodule::clear_error() int colvarmodule::error(std::string const &message, int code) { - set_error_bits(code); + set_error_bits(code >= 0 ? code : COLVARS_ERROR); std::string const trailing_newline = (message.size() > 0) ? (message[message.size()-1] == '\n' ? "" : "\n") : ""; diff --git a/src/colvarmodule.h b/src/colvarmodule.h index 236d432a9..bc4855225 100644 --- a/src/colvarmodule.h +++ b/src/colvarmodule.h @@ -33,16 +33,6 @@ You can browse the class hierarchy or the list of source files. /// shared between all object instances) to be accessed from other /// objects. -#define COLVARS_OK 0 -#define COLVARS_ERROR 1 -#define COLVARS_NOT_IMPLEMENTED (1<<1) -#define COLVARS_INPUT_ERROR (1<<2) // out of bounds or inconsistent input -#define COLVARS_BUG_ERROR (1<<3) // Inconsistent state indicating bug -#define COLVARS_FILE_ERROR (1<<4) -#define COLVARS_MEMORY_ERROR (1<<5) -#define COLVARS_NO_SUCH_FRAME (1<<6) // Cannot load the requested frame - -#include #include #include #include @@ -190,7 +180,9 @@ class colvarmodule { template class matrix2d; class quaternion; class rotation; + class usage; + class memory_stream; /// Residue identifier typedef int residue_id; @@ -247,6 +239,8 @@ class colvarmodule { return it; } + bool binary_restart; + /// \brief Finite difference step size (if there is no dynamics, or /// if gradients need to be tested independently from the size of /// dt) @@ -449,17 +443,49 @@ class colvarmodule { /// (Re)initialize the output trajectory and state file (does not write it yet) int setup_output(); - /// Read a restart file - std::istream & read_restart(std::istream &is); +private: + + template IST & read_state_template_(IST &is); + + /// Internal state buffer, to be read as an unformatted stream + std::vector input_state_buffer_; + +public: + + /// Read all objects' state fron a formatted (text) stream + std::istream & read_state(std::istream &is); + + /// Read all objects' state fron an unformatted (binary) stream + memory_stream & read_state(memory_stream &is); + + /// Set an internal state buffer, to be read later as an unformatted stream when ready + int set_input_state_buffer(size_t n, unsigned char *buf); + + /// Same as set_input_state_buffer() for C array, but uses std::move + int set_input_state_buffer(std::vector &buf); /// Read the states of individual objects; allows for changes std::istream & read_objects_state(std::istream &is); + /// Read the states of individual objects; allows for changes + memory_stream & read_objects_state(memory_stream &is); + /// If needed (old restart file), print the warning that cannot be ignored int print_total_forces_errning(bool warn_total_forces); - /// Write the output restart file - std::ostream & write_restart(std::ostream &os); +private: + template OST &write_state_template_(OST &os); + +public: + + /// Write the state of the module to a formatted (text) file + std::ostream & write_state(std::ostream &os); + + /// Write the state of the module to an unformatted (binary) file + memory_stream & write_state(memory_stream &os); + + /// Write the state of the module to an array of bytes (wrapped as a memory_stream object) + int write_state_buffer(std::vector &buffer); /// Strips .colvars.state from filename and checks that it is not empty static std::string state_file_prefix(char const *filename); @@ -650,7 +676,7 @@ class colvarmodule { static void log(std::string const &message, int min_log_level = 10); /// Print a message to the main log and set global error code - static int error(std::string const &message, int code = COLVARS_ERROR); + static int error(std::string const &message, int code = -1); private: @@ -838,9 +864,20 @@ class colvarmodule { typedef colvarmodule cvm; - std::ostream & operator << (std::ostream &os, cvm::rvector const &v); std::istream & operator >> (std::istream &is, cvm::rvector &v); +namespace { + constexpr int32_t COLVARS_OK = 0; + constexpr int32_t COLVARS_ERROR = 1; + constexpr int32_t COLVARS_NOT_IMPLEMENTED = (1<<1); + constexpr int32_t COLVARS_INPUT_ERROR = (1<<2); // out of bounds or inconsistent input + constexpr int32_t COLVARS_BUG_ERROR = (1<<3); // Inconsistent state indicating bug + constexpr int32_t COLVARS_FILE_ERROR = (1<<4); + constexpr int32_t COLVARS_MEMORY_ERROR = (1<<5); + constexpr int32_t COLVARS_NO_SUCH_FRAME = (1<<6); // Cannot load the requested frame +} + + #endif diff --git a/src/colvarparse.cpp b/src/colvarparse.cpp index 4146300c1..c189a9e89 100644 --- a/src/colvarparse.cpp +++ b/src/colvarparse.cpp @@ -14,6 +14,7 @@ #include "colvarmodule.h" #include "colvarvalue.h" #include "colvarparse.h" +#include "colvars_memstream.h" // space & tab @@ -866,55 +867,107 @@ colvarparse::read_block::~read_block() std::istream & operator>> (std::istream &is, colvarparse::read_block const &rb) { - std::streampos start_pos = is.tellg(); - std::string read_key, next; + auto start_pos = is.tellg(); - if ( !(is >> read_key) || !(read_key == rb.key) || - !(is >> next) ) { - // the requested keyword has not been found, or it is not possible - // to read data after it + std::string read_key; + if ( !(is >> read_key) || !(read_key == rb.key) ) { + // the requested keyword has not been found is.clear(); - is.seekg(start_pos, std::ios::beg); + is.seekg(start_pos); is.setstate(std::ios::failbit); return is; } - if (next != "{") { - if (rb.data) { - *(rb.data) = next; + std::string next; + if (is >> next) { + if (next == "{") { + // Parse a formatted brace-delimited block + rb.read_block_contents(is); + } else { + if (rb.data) { + *(rb.data) = next; + } } - return is; + } else { + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::badbit); } - size_t brace_count = 1; + return is; +} + + +std::istream &colvarparse::read_block::read_block_contents(std::istream &is, + bool block_only) const +{ + int brace_count = block_only ? 0 : 1; + auto const start_pos = is.tellg(); std::string line; while (colvarparse::getline_nocomments(is, line)) { size_t br = 0, br_old = 0; - while ( (br = line.find_first_of("{}", br)) != std::string::npos) { - if (line[br] == '{') brace_count++; - if (line[br] == '}') brace_count--; + while ((br = line.find_first_of("{}", br)) != std::string::npos) { + if (line[br] == '{') + brace_count++; + if (line[br] == '}') + brace_count--; br_old = br; br++; } - if (brace_count) { - if (rb.data) { - (rb.data)->append(line + "\n"); + if (brace_count || block_only) { + // Add whole line if (1) brace are unmatched or (2) we're reading the whole stream anyway + if (data) { + data->append(line + "\n"); } - } - else { - if (rb.data) { - (rb.data)->append(line, 0, br_old); + } else { + // Not reading whole block and braces are matched; add until before the last brace + if (data) { + data->append(line.substr(0, br_old) + "\n"); } break; } } - if (brace_count) { - // end-of-file reached - // restore initial position + + if (block_only) { + if (is.rdstate() & std::ios::eofbit) { + // Clear EOF errors if we were meant to read the whole block + is.clear(); + } + } else { + if (brace_count) { + // Could not match braces, restore initial position and set fail bit + is.clear(); + is.seekg(start_pos); + is.setstate(std::ios::failbit); + } + } + + return is; +} + + +cvm::memory_stream &operator>>(cvm::memory_stream &is, colvarparse::read_block const &rb) +{ + auto const start_pos = is.tellg(); + + std::string read_key; + if ( !(is >> read_key) || !(read_key == rb.key) ) { + // the requested keyword has not been found is.clear(); - is.seekg(start_pos, std::ios::beg); + is.seekg(start_pos); is.setstate(std::ios::failbit); + return is; } + + std::string content; + if (is >> content) { + std::istringstream iss(content); + if (!rb.read_block_contents(iss, true)) { + is.seekg(start_pos); + is.setstate(std::ios::failbit); + } + } + return is; } diff --git a/src/colvarparse.h b/src/colvarparse.h index 447651609..402997a31 100644 --- a/src/colvarparse.h +++ b/src/colvarparse.h @@ -267,28 +267,36 @@ class colvarparse : public colvarparams { return out; } - /// \brief Helper class to read a block of the type "key { ... }" - /// from a stream and store it in a string + /// Helper class to read a block "key { ... }" from a stream and store it in a string /// - /// Useful on restarts, where the file is too big to be loaded in a - /// string by key_lookup; it can only check that the keyword is - /// correct and the block is properly delimited by braces, not - /// skipping other blocks + /// Useful on restarts, where the file is too big to be loaded in a string + /// by key_lookup(); it can only check that the keyword is correct and the + /// block is properly delimited by braces, not skipping other blocks class read_block { - - /// The keyword that identifies the block - std::string const key; - - /// Where to keep the data (may be NULL) - std::string * const data; - public: - read_block(std::string const &key_in, std::string *data_in = nullptr); + read_block(std::string const &key, std::string *data = nullptr); ~read_block(); + /// Read block from stream, first check that key matches, then call read_contents() friend std::istream & operator >> (std::istream &is, read_block const &rb); + + /// Read block from stream, first check that key matches, then call read_contents() + friend cvm::memory_stream & operator >> (cvm::memory_stream &is, read_block const &rb); + + private: + + /// Keyword that identifies the block + std::string const key; + + /// Where to keep the data + std::string * const data; + + /// Read the contents of a formatted block after checking that the keyword matches + /// \param[in] is Stream object + /// \param[in] block_only If true, stream is assumed to contain only the block without braces + std::istream & read_block_contents(std::istream &is, bool block_only = false) const; }; diff --git a/src/colvarproxy_io.cpp b/src/colvarproxy_io.cpp index 7e7cd3829..e3039afb8 100644 --- a/src/colvarproxy_io.cpp +++ b/src/colvarproxy_io.cpp @@ -358,7 +358,7 @@ std::ostream & colvarproxy_io::output_stream(std::string const &output_name, backup_file(output_name.c_str()); - output_streams_[output_name] = new std::ofstream(output_name.c_str()); + output_streams_[output_name] = new std::ofstream(output_name.c_str(), std::ios::binary); if (!*(output_streams_[output_name])) { cvm::error("Error: cannot write to "+description+" \""+output_name+"\".\n", COLVARS_FILE_ERROR); diff --git a/src/colvars_memstream.cpp b/src/colvars_memstream.cpp new file mode 100644 index 000000000..13cb8fb34 --- /dev/null +++ b/src/colvars_memstream.cpp @@ -0,0 +1,102 @@ +// -*- c++ -*- + +// This file is part of the Collective Variables module (Colvars). +// The original version of Colvars and its updates are located at: +// https://github.com/Colvars/colvars +// Please update all Colvars source files before making any changes. +// If you wish to distribute your changes, please submit them to the +// Colvars repository at GitHub. + + +#include "colvarmodule.h" +#include "colvartypes.h" +#include "colvarvalue.h" +#include "colvars_memstream.h" + + +bool cvm::memory_stream::expand_output_buffer(size_t add_bytes) +{ + auto &buffer = external_output_buffer_ ? *external_output_buffer_ : internal_buffer_; + if ((buffer.size() + add_bytes) <= max_length_) { + buffer.resize((buffer.size() + add_bytes)); + } else { + setstate(std::ios::badbit); + } + return bool(*this); +} + + +template <> void cvm::memory_stream::write_object(std::string const &t) +{ + size_t const string_length = t.size(); + size_t const new_data_size = sizeof(size_t) + sizeof(char) * string_length; + if (expand_output_buffer(new_data_size)) { + std::memcpy(output_location(), &string_length, sizeof(size_t)); + incr_write_pos(sizeof(size_t)); + std::memcpy(output_location(), t.c_str(), t.size() * sizeof(char)); + incr_write_pos(t.size() * sizeof(char)); + } +} + +template <> cvm::memory_stream &operator<<(cvm::memory_stream &os, std::string const &t) +{ + os.write_object(t); + return os; +} + +template <> void cvm::memory_stream::write_object(colvarvalue const &t) +{ + *this << t; +} + +template <> void cvm::memory_stream::write_object(cvm::vector1d const &t) +{ + return write_vector(t.data_array()); +} + +template <> +cvm::memory_stream &operator<<(cvm::memory_stream &os, cvm::vector1d const &t) +{ + os.write_vector(t.data_array()); + return os; +} + + +template <> void cvm::memory_stream::read_object(std::string &t) +{ + begin_reading(); + size_t string_length = 0; + if (has_remaining(sizeof(size_t))) { + std::memcpy(&string_length, input_location(), sizeof(size_t)); + incr_read_pos(sizeof(size_t)); + if (has_remaining(string_length * sizeof(char))) { + t.assign(reinterpret_cast(input_location()), string_length); + incr_read_pos(string_length * sizeof(char)); + done_reading(); + } else { + setstate(std::ios::failbit); + } + } +} + +template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, std::string &t) +{ + is.read_object(t); + return is; +} + +template <> void cvm::memory_stream::read_object(colvarvalue &t) +{ + *this >> t; +} + +template <> void cvm::memory_stream::read_object(cvm::vector1d &t) +{ + return read_vector(t.data_array()); +} + +template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, cvm::vector1d &t) +{ + is.read_vector(t.data_array()); + return is; +} diff --git a/src/colvars_memstream.h b/src/colvars_memstream.h new file mode 100644 index 000000000..0d80d2794 --- /dev/null +++ b/src/colvars_memstream.h @@ -0,0 +1,289 @@ +// -*- c++ -*- + +// This file is part of the Collective Variables module (Colvars). +// The original version of Colvars and its updates are located at: +// https://github.com/Colvars/colvars +// Please update all Colvars source files before making any changes. +// If you wish to distribute your changes, please submit them to the +// Colvars repository at GitHub. + +#ifndef MEMORY_STREAM_H +#define MEMORY_STREAM_H + +#include +#include +#include +#include +#include + + +// Work around missing std::is_trivially_copyable in old GCC and Clang versions +// TODO remove this after CentOS 7 has been beyond EOL for a while +#if (defined(__GNUC__) && (__GNUC__ < 5) && !defined(__clang__)) || (defined(__clang__) && (__clang_major__ < 7)) +// Clang needs an exception, because it defines __GNUC__ as well +#define IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T) +#else +#define IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable::value +#endif + + +class cvm::memory_stream { + +public: + + /// Set up an empty stream with an internal buffer, suitable for writing to + /// \param max_length Maximum allowed capacity (default is 64 GiB) + memory_stream(size_t max_length = (static_cast(1L) << 36)) : max_length_(max_length) {} + + /// Set up a stream based on an external input buffer + memory_stream(size_t n, unsigned char const *buf) + : external_input_buffer_(buf), internal_buffer_(), data_length_(n), max_length_(data_length_) + { + } + + /// Set up a stream based on an external output buffer + memory_stream(std::vector &buf) : memory_stream() + { + external_output_buffer_ = &buf; + } + + /// Length of the buffer + inline size_t length() const { return data_length_; } + + /// Output buffer + inline unsigned char *output_buffer() + { + return (external_output_buffer_ ? external_output_buffer_->data() : internal_buffer_.data()); + } + + /// Next location to write to + inline unsigned char *output_location() { return output_buffer() + data_length_; } + + /// Input buffer + inline unsigned char const *input_buffer() const + { + return (external_input_buffer_ ? external_input_buffer_ : internal_buffer_.data()); + } + + /// Next location to read from + inline unsigned char const *input_location() const { return input_buffer() + read_pos_; } + + /// Cast operator to be used to test for errors + inline explicit operator bool() const { return state_ == std::ios::goodbit; } + + /// Write a simple object to the output buffer + template void write_object(T const &t); + + /// Wrapper to write_object() + template friend memory_stream &operator<<(memory_stream &os, T const &t); + + /// Write a vector of simple objects to the output buffer + template void write_vector(std::vector const &t); + + /// Wrapper to write_vector() + template + friend memory_stream &operator<<(memory_stream &os, std::vector const &t); + + /// Read a simple object from the buffer + template void read_object(T &t); + + /// Wrapper to read_object() + template friend memory_stream &operator>>(memory_stream &is, T &t); + + /// Read a vector of simple objects from the buffer + template void read_vector(std::vector &t); + + /// Wrapper to read_vector() + template friend memory_stream &operator>>(memory_stream &is, std::vector &t); + + + // Compatibility with STL stream functions + + /// Report the current position in the buffer + inline size_t tellg() const { return read_pos_; } + + /// Report the current position in the buffer + inline memory_stream & seekg(size_t pos) { read_pos_ = pos; return *this; } + + /// Ignore formatting operators + inline void setf(decltype(std::ios::fmtflags(0)), decltype(std::ios::floatfield)) {} + + /// Ignore formatting operators + inline void flags(decltype(std::ios::fmtflags(0))) {} + + /// Get the current formatting flags (i.e. none because this stream is unformatted) + inline decltype(std::ios::fmtflags(0)) flags() const { return std::ios::fmtflags(0); } + + /// Get the error code + inline std::ios::iostate rdstate() const { return state_; } + + /// Set the error code + inline void setstate(std::ios::iostate new_state) { state_ |= new_state; } + + /// Clear the error code + inline void clear() { state_ = std::ios::goodbit; } + +protected: + + /// External output buffer + std::vector *external_output_buffer_ = nullptr; + + /// External input buffer + unsigned char const *external_input_buffer_ = nullptr; + + /// Internal buffer (may server for both input and output) + std::vector internal_buffer_; + + /// Length of the data buffer (either internal or external) + size_t data_length_ = 0L; + + /// Largest allowed capacity of the data buffer + size_t const max_length_; + + /// Error status + std::ios::iostate state_ = std::ios::goodbit; + + /// Add the requester number of bytes to the array capacity; return false if buffer is external + bool expand_output_buffer(size_t add_bytes); + + /// Move the buffer position past the data just written + inline void incr_write_pos(size_t c) { data_length_ += c; } + + /// Current position when reading from the buffer + size_t read_pos_ = 0L; + + /// Begin an attempt to read an object; assume EOF unless there is space remaining + inline void begin_reading() { setstate(std::ios::eofbit); } + + /// Mark the reading attempt succesful + inline void done_reading() { clear(); } + + /// Move the buffer position past the data just read + inline void incr_read_pos(size_t c) { read_pos_ += c; } + + /// Check that the buffer contains enough bytes to read as the argument says, set error + /// otherwise + inline bool has_remaining(size_t c) { return c <= (data_length_ - read_pos_); } + }; + +template void cvm::memory_stream::write_object(T const &t) +{ + static_assert(IS_TRIVIALLY_COPYABLE(T), "Cannot use write_object() on complex type"); + size_t const new_data_size = sizeof(T); + if (expand_output_buffer(new_data_size)) { + std::memcpy(output_location(), &t, sizeof(T)); + incr_write_pos(new_data_size); + } +} + +template cvm::memory_stream &operator<<(cvm::memory_stream &os, T const &t) +{ + os.write_object(t); + return os; +} + +template void cvm::memory_stream::write_vector(std::vector const &t) +{ + static_assert(IS_TRIVIALLY_COPYABLE(T), "Cannot use write_vector() on complex type"); + size_t const vector_length = t.size(); + size_t const new_data_size = sizeof(size_t) + sizeof(T) * vector_length; + if (expand_output_buffer(new_data_size)) { + std::memcpy(output_location(), &vector_length, sizeof(size_t)); + incr_write_pos(sizeof(T)); + std::memcpy(output_location(), t.data(), t.size() * sizeof(T)); + incr_write_pos(t.size() * sizeof(T)); + } +} + +template +cvm::memory_stream &operator<<(cvm::memory_stream &os, std::vector const &t) +{ + os.write_vector(t); + return os; +} + +template void cvm::memory_stream::read_object(T &t) +{ + static_assert(IS_TRIVIALLY_COPYABLE(T), "Cannot use read_object() on complex type"); + begin_reading(); + if (has_remaining(sizeof(T))) { + std::memcpy(&t, input_location(), sizeof(T)); + incr_read_pos(sizeof(T)); + done_reading(); + } +} + +template cvm::memory_stream &operator>>(cvm::memory_stream &is, T &t) +{ + is.read_object(t); + return is; +} + +template void cvm::memory_stream::read_vector(std::vector &t) +{ + static_assert(IS_TRIVIALLY_COPYABLE(T), "Cannot use read_vector() on complex type"); + begin_reading(); + size_t vector_length = 0; + if (has_remaining(sizeof(size_t))) { + std::memcpy(&vector_length, input_location(), sizeof(size_t)); + incr_read_pos(sizeof(size_t)); + if (has_remaining(vector_length * sizeof(T))) { + t.resize(vector_length); + std::memcpy(t.data(), input_location(), vector_length * sizeof(T)); + incr_read_pos(vector_length * sizeof(T)); + done_reading(); + } else { + setstate(std::ios::failbit); + } + } +} + +template cvm::memory_stream &operator>>(cvm::memory_stream &is, std::vector &t) +{ + is.read_vector(t); + return is; +} + +template cvm::memory_stream &operator<<(cvm::memory_stream &os, + decltype(std::setprecision(10)) const &) +{ + return os; +} + +#if !defined(_MSC_VER) && !defined(__SUNPRO_CC) +// Visual Studio and MSVC use the same return type for both modifiers +template cvm::memory_stream &operator<<(cvm::memory_stream &os, + decltype(std::setw(10)) const &) +{ + return os; +} +#endif + +// Declare specializations + +template <> void cvm::memory_stream::write_object(std::string const &t); + +template <> cvm::memory_stream &operator<<(cvm::memory_stream &os, std::string const &t); + +template <> void cvm::memory_stream::write_object(colvarvalue const &t); + +template <> cvm::memory_stream &operator<<(cvm::memory_stream &os, colvarvalue const &x); + +template <> void cvm::memory_stream::write_object(cvm::vector1d const &t); + +template <> +cvm::memory_stream &operator<<(cvm::memory_stream &os, cvm::vector1d const &t); + +template <> void cvm::memory_stream::read_object(std::string &t); + +template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, std::string &t); + +template <> void cvm::memory_stream::read_object(colvarvalue &t); + +template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, colvarvalue &t); + +template <> void cvm::memory_stream::read_object(cvm::vector1d &t); + +template <> cvm::memory_stream &operator>>(cvm::memory_stream &is, cvm::vector1d &t); + +#endif diff --git a/src/colvartypes.h b/src/colvartypes.h index aa0c388af..455e628f1 100644 --- a/src/colvartypes.h +++ b/src/colvartypes.h @@ -10,6 +10,7 @@ #ifndef COLVARTYPES_H #define COLVARTYPES_H +#include // TODO specialize templates and replace this with iosfwd #include #ifdef COLVARS_LAMMPS @@ -80,6 +81,12 @@ template class colvarmodule::vector1d return data; } + /// Return a reference to the data + inline std::vector const &data_array() const + { + return data; + } + inline ~vector1d() { data.clear(); @@ -504,6 +511,12 @@ template class colvarmodule::matrix2d return data; } + /// Return a reference to the data + inline std::vector const &data_array() const + { + return data; + } + inline row & operator [] (size_t const i) { return rows[i]; diff --git a/src/colvarvalue.cpp b/src/colvarvalue.cpp index e57859dfa..64436db98 100644 --- a/src/colvarvalue.cpp +++ b/src/colvarvalue.cpp @@ -13,6 +13,7 @@ #include "colvarmodule.h" #include "colvarvalue.h" +#include "colvars_memstream.h" @@ -721,29 +722,43 @@ int colvarvalue::from_simple_string(std::string const &s) return COLVARS_ERROR; } -std::ostream & operator << (std::ostream &os, colvarvalue const &x) + +template void colvarvalue::write_to_stream_template_(OST &os) const { - switch (x.type()) { + switch (type()) { case colvarvalue::type_scalar: - os << x.real_value; + os << real_value; break; case colvarvalue::type_3vector: case colvarvalue::type_unit3vector: case colvarvalue::type_unit3vectorderiv: - os << x.rvector_value; + os << rvector_value; break; case colvarvalue::type_quaternion: case colvarvalue::type_quaternionderiv: - os << x.quaternion_value; + os << quaternion_value; break; case colvarvalue::type_vector: - os << x.vector1d_value; + os << vector1d_value; break; case colvarvalue::type_notset: default: os << "not set"; break; } +} + + +std::ostream & operator << (std::ostream &os, colvarvalue const &x) +{ + x.write_to_stream_template_(os); + return os; +} + + +cvm::memory_stream & operator << (cvm::memory_stream &os, colvarvalue const &x) +{ + x.write_to_stream_template_(os); return os; } @@ -758,44 +773,55 @@ std::ostream & operator << (std::ostream &os, std::vector const &v) } -std::istream & operator >> (std::istream &is, colvarvalue &x) +template void colvarvalue::read_from_stream_template_(IST &is) { - if (x.type() == colvarvalue::type_notset) { + if (type() == colvarvalue::type_notset) { cvm::error("Trying to read from a stream a colvarvalue, " "which has not yet been assigned a data type.\n"); - return is; } - switch (x.type()) { + switch (type()) { case colvarvalue::type_scalar: - is >> x.real_value; + is >> real_value; break; case colvarvalue::type_3vector: case colvarvalue::type_unit3vectorderiv: - is >> x.rvector_value; + is >> rvector_value; break; case colvarvalue::type_unit3vector: - is >> x.rvector_value; - x.apply_constraints(); + is >> rvector_value; + apply_constraints(); break; case colvarvalue::type_quaternion: - is >> x.quaternion_value; - x.apply_constraints(); + is >> quaternion_value; + apply_constraints(); break; case colvarvalue::type_quaternionderiv: - is >> x.quaternion_value; + is >> quaternion_value; break; case colvarvalue::type_vector: - is >> x.vector1d_value; + is >> vector1d_value; break; case colvarvalue::type_notset: default: - x.undef_op(); + undef_op(); } +} + + +std::istream & operator >> (std::istream &is, colvarvalue &x) +{ + x.read_from_stream_template_(is); return is; } +cvm::memory_stream & operator >> (cvm::memory_stream &is, colvarvalue &x) +{ + x.read_from_stream_template_(is); + return is; +} + size_t colvarvalue::output_width(size_t const &real_width) const { switch (this->value_type) { diff --git a/src/colvarvalue.h b/src/colvarvalue.h index 6bc7a57c7..5670906cd 100644 --- a/src/colvarvalue.h +++ b/src/colvarvalue.h @@ -298,12 +298,31 @@ class colvarvalue { /// Undefined operation void undef_op() const; +private: - /// \brief Formatted output operator - friend std::ostream & operator << (std::ostream &os, colvarvalue const &q); + /// Generic stream writing function (formatted and not) + template void write_to_stream_template_(OST &os) const; - /// \brief Formatted input operator - friend std::istream & operator >> (std::istream &is, colvarvalue &q); +public: + + /// Formatted output operator + friend std::ostream & operator << (std::ostream &os, colvarvalue const &x); + + /// Unformatted output operator + friend cvm::memory_stream & operator << (cvm::memory_stream &os, colvarvalue const &x); + +private: + + /// Generic stream reading function (formatted and not) + template void read_from_stream_template_(IST &is); + +public: + + /// Formatted input operator + friend std::istream & operator >> (std::istream &is, colvarvalue &x); + + /// Unformatted input operator + friend cvm::memory_stream & operator >> (cvm::memory_stream &is, colvarvalue &x); /// Give the number of characters required to output this /// colvarvalue, given the current type assigned and the number of diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt index e2804ae75..84d3160f8 100644 --- a/tests/unittests/CMakeLists.txt +++ b/tests/unittests/CMakeLists.txt @@ -1,12 +1,18 @@ -add_executable(colvarvalue_unit3vector colvarvalue_unit3vector.cpp) -target_link_libraries(colvarvalue_unit3vector PRIVATE colvars) -target_include_directories(colvarvalue_unit3vector PRIVATE ${COLVARS_SOURCE_DIR}/src) -add_test(NAME colvarvalue_unit3vector COMMAND colvarvalue_unit3vector) +set(COLVARS_STUBS_DIR ${COLVARS_SOURCE_DIR}/misc_interfaces/stubs/) -add_executable(file_io file_io.cpp) -target_link_libraries(file_io PRIVATE colvars) -target_include_directories(file_io PRIVATE ${COLVARS_SOURCE_DIR}/src) -add_test(NAME file_io COMMAND file_io) +foreach(CMD + colvarvalue_unit3vector + file_io + memory_stream + read_xyz_traj + ) + add_executable(${CMD} ${CMD}.cpp) + target_link_libraries(${CMD} PRIVATE colvars) + target_include_directories(${CMD} PRIVATE ${COLVARS_SOURCE_DIR}/src) + target_include_directories(${CMD} PRIVATE ${COLVARS_STUBS_DIR}) + target_link_libraries(${CMD} PRIVATE colvars colvars_stubs) + add_test(NAME ${CMD} COMMAND ${CMD}) +endforeach() if(COLVARS_TCL) add_executable(embedded_tcl embedded_tcl.cpp) @@ -15,13 +21,6 @@ if(COLVARS_TCL) add_test(NAME embedded_tcl COMMAND embedded_tcl) endif() -# Last test requires the stubs proxy -add_executable(read_xyz_traj read_xyz_traj.cpp) -set(COLVARS_STUBS_DIR ${COLVARS_SOURCE_DIR}/misc_interfaces/stubs/) -target_link_libraries(read_xyz_traj PRIVATE colvars colvars_stubs) -target_include_directories(read_xyz_traj PRIVATE ${COLVARS_SOURCE_DIR}/src) -target_include_directories(read_xyz_traj PRIVATE ${COLVARS_STUBS_DIR}) -add_test(NAME read_xyz_traj COMMAND read_xyz_traj) add_custom_command( TARGET read_xyz_traj POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink diff --git a/tests/unittests/memory_stream.cpp b/tests/unittests/memory_stream.cpp new file mode 100644 index 000000000..915c2142b --- /dev/null +++ b/tests/unittests/memory_stream.cpp @@ -0,0 +1,107 @@ +// -*- c++ -*- + +#include + +#include "colvarmodule.h" +#include "colvartypes.h" +#include "colvarvalue.h" +#include "colvarparse.h" +#include "colvars_memstream.h" + + +template std::ostream &operator<<(std::ostream &os, std::vector const &v) +{ + os << "{"; + for (auto &vi : v) + os << " " << vi; + os << " }"; + return os; +} + +template void read_and_print(cvm::memory_stream &is, T &t) +{ + if (is >> t) { + std::cout << typeid(T).name() << ": " << t << std::endl; + } +} + + +int main(int argc, char const *argv[]) +{ + cvm::real x = -101.0; + size_t count = 1240566; + std::vector const a{1, 2, 3, 4, 5, 6, 7, 8}; + cvm::rvector const v = (cvm::rvector(-1.0, 1.0, 0.5)).unit(); + std::string const text("Vabbé / Está bien / Ça va / 好吧"); + std::vector> const a2(1, a); + + cvm::vector1d v_from_a(a.size(), a.data()); + + colvarvalue cv(v, colvarvalue::type_unit3vector); + + size_t n = (1L << 26); + if (argc > 1) { + n = atoi(argv[1]); + } + + cvm::memory_stream buf(n); + + // Use standard functions and operators interchangeably + buf.write_object(x); + buf.write_object(count); + buf.write_object(v); + buf << text; + buf << a; + + buf << v_from_a; + + buf << cv; + + std::string const block_key("colvar"); + std::string const block(" name a_colvar\n" + " x 1.0\n"); + buf << block_key << block; + + // // The following will raise a compile error + // buf << a2; + + if (buf) { + + cvm::real new_x = 0.0; + read_and_print(buf, new_x); + + size_t new_count = 0; + read_and_print(buf, new_count); + + cvm::rvector new_v; + read_and_print(buf, new_v); + + std::string new_text; + read_and_print(buf, new_text); + + std::vector new_a; + read_and_print(buf, new_a); + + cvm::vector1d new_v_from_a; + read_and_print(buf, new_v_from_a); + + colvarvalue new_cv(colvarvalue::type_unit3vector); + read_and_print(buf, new_cv); + + std::string block; + buf >> colvarparse::read_block("colvar", &block); + if (block.size()) { + std::cout << "block: \"\n" << block << "\"" << std::endl; + } + } + + std::cout << "buf.tellg() = " << buf.tellg() << std::endl; + std::cout << "buf.length() = " << buf.length() << std::endl; + std::cout << "buf.rdstate() = " << buf.rdstate() << std::endl; + std::cout << "goodbit = " << std::ios::goodbit << std::endl; + std::cout << "eofbit = " << std::ios::eofbit << std::endl; + std::cout << "badbit = " << std::ios::badbit << std::endl; + std::cout << "failbit = " << std::ios::failbit << std::endl; + + return buf ? 0 : 1; +} diff --git a/vmd/src/colvars_files.pl b/vmd/src/colvars_files.pl index ec2c0f6f9..b6bfdbf2a 100644 --- a/vmd/src/colvars_files.pl +++ b/vmd/src/colvars_files.pl @@ -49,6 +49,7 @@ 'colvarscript_commands.C', 'colvarscript_commands_bias.C', 'colvarscript_commands_colvar.C', + 'colvars_memstream.C', 'colvartypes.C', 'colvarvalue.C', 'nr_jacobi.C'); @@ -85,6 +86,7 @@ 'colvarscript_commands.h', 'colvarscript_commands_bias.h', 'colvarscript_commands_colvar.h', + 'colvars_memstream.h', 'colvars_version.h', 'colvartypes.h', 'colvarvalue.h',