+/* This function computes two components of the estimate of the variance
+ * in the displacement of one atom in a system of two constrained atoms.
+ * Returns in sigma2_2d the variance due to rotation of the constrained
+ * atom around the atom to which it constrained.
+ * Returns in sigma2_3d the variance due to displacement of the COM
+ * of the whole system of the two constrained atoms.
+ *
+ * Note that we only take a single constraint (the one to the heaviest atom)
+ * into account. If an atom has multiple constraints, this will result in
+ * an overestimate of the displacement, which gives a larger drift and buffer.
+ */
+static void constrained_atom_sigma2(real kT_fac,
+ const atom_nonbonded_kinetic_prop_t *prop,
+ real *sigma2_2d,
+ real *sigma2_3d)
+{
+ real sigma2_rot;
+ real com_dist;
+ real sigma2_rel;
+ real scale;
+
+ /* Here we decompose the motion of a constrained atom into two
+ * components: rotation around the COM and translation of the COM.
+ */
+
+ /* Determine the variance for the displacement of the rotational mode */
+ sigma2_rot = kT_fac/(prop->mass*(prop->mass + prop->con_mass)/prop->con_mass);
+
+ /* The distance from the atom to the COM, i.e. the rotational arm */
+ com_dist = prop->con_len*prop->con_mass/(prop->mass + prop->con_mass);
+
+ /* The variance relative to the arm */
+ sigma2_rel = sigma2_rot/(com_dist*com_dist);
+ /* At 6 the scaling formula has slope 0,
+ * so we keep sigma2_2d constant after that.
+ */
+ if (sigma2_rel < 6)
+ {
+ /* A constrained atom rotates around the atom it is constrained to.
+ * This results in a smaller linear displacement than for a free atom.
+ * For a perfectly circular displacement, this lowers the displacement
+ * by: 1/arcsin(arc_length)
+ * and arcsin(x) = 1 + x^2/6 + ...
+ * For sigma2_rel<<1 the displacement distribution is erfc
+ * (exact formula is provided below). For larger sigma, it is clear
+ * that the displacement can't be larger than 2*com_dist.
+ * It turns out that the distribution becomes nearly uniform.
+ * For intermediate sigma2_rel, scaling down sigma with the third
+ * order expansion of arcsin with argument sigma_rel turns out
+ * to give a very good approximation of the distribution and variance.
+ * Even for larger values, the variance is only slightly overestimated.
+ * Note that the most relevant displacements are in the long tail.
+ * This rotation approximation always overestimates the tail (which
+ * runs to infinity, whereas it should be <= 2*com_dist).
+ * Thus we always overestimate the drift and the buffer size.
+ */
+ scale = 1/(1 + sigma2_rel/6);
+ *sigma2_2d = sigma2_rot*scale*scale;
+ }
+ else
+ {
+ /* sigma_2d is set to the maximum given by the scaling above.
+ * For large sigma2 the real displacement distribution is close
+ * to uniform over -2*con_len to 2*com_dist.
+ * Our erfc with sigma_2d=sqrt(1.5)*com_dist (which means the sigma
+ * of the erfc output distribution is con_dist) overestimates
+ * the variance and additionally has a long tail. This means
+ * we have a (safe) overestimation of the drift.
+ */
+ *sigma2_2d = 1.5*com_dist*com_dist;
+ }
+
+ /* The constrained atom also moves (in 3D) with the COM of both atoms */
+ *sigma2_3d = kT_fac/(prop->mass + prop->con_mass);
+}
+
+static void get_atom_sigma2(real kT_fac,
+ const atom_nonbonded_kinetic_prop_t *prop,
+ real *sigma2_2d,
+ real *sigma2_3d)
+{
+ if (prop->bConstr)
+ {
+ /* Complicated constraint calculation in a separate function */
+ constrained_atom_sigma2(kT_fac, prop, sigma2_2d, sigma2_3d);
+ }
+ else
+ {
+ /* Unconstrained atom: trivial */
+ *sigma2_2d = 0;
+ *sigma2_3d = kT_fac/prop->mass;
+ }
+}
+
+static void approx_2dof(real s2, real x, real *shift, real *scale)