Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: try to solve #87 for non-scala components #713

Merged
merged 14 commits into from
Oct 15, 2024
Merged

Conversation

HanatoK
Copy link
Member

@HanatoK HanatoK commented Sep 12, 2024

We have already had the algorithm for computing the gradients of the fitting group from the gradients of the main group. In cases that the gradients of the main group are not available, we can "intercept" the forces applied to the atoms of the main group, and then use the same algorithm to convert these forces (with the rotation), to the forces applied to the atoms of the fitting group.

I have tried with a custom test system from NANMA and the applied forces (by setting COLVARS_DEBUG to true) seems to be correct and consistent with what I get from pytorch autograd, but still need to find a way to verify with the a test example like 000_orientation-fitgroup_harmonic-ori-fixed.

The python script using pytorch is:

#!/usr/bin/env python3
import torch

torch.set_printoptions(precision=10)

ref_atom_positions = torch.tensor(
    [[-3.03000000000000e-01,  6.58100000000000e+00, -4.08600000000000e+00],
     [-3.40992481583554e-01,  5.25999424393448e+00, -3.84398945431947e+00],
     [-1.14000656872228e+00,  4.31599997645619e+00, -4.55197544380884e+00],
     [-2.64300000000000e+00,  4.54100000000000e+00, -4.47400000000000e+00],
     [ 3.66040236087398e-01,  7.51902138187147e+00, -3.14598245241798e+00],
     [-7.80000000000000e-01,  7.10700000000000e+00, -5.09200000000000e+00],
     [-8.41933440137392e-01,  2.84501270558389e+00, -4.06300150484121e+00],
     [-3.13700000000000e+00,  5.20300000000000e+00, -3.61100000000000e+00],
     [-3.36600191229170e+00,  3.86900202368733e+00, -5.39299756043564e+00],
     [-4.84201397015324e+00,  3.75000258115307e+00, -5.30396344423208e+00]],
    dtype=torch.float64)

atom_positions = torch.tensor(
    [[-2.60879361204690e-01 ,  6.45904346634443e+00 , -4.16194558517708e+00],
     [-2.21193435449785e-01 ,  5.13402361114321e+00 , -4.05438074318753e+00],
     [-1.09763690111340e+00 ,  4.21484233222457e+00 , -4.69460450887656e+00],
     [-2.59243071716164e+00 ,  4.53145098281633e+00 , -4.62989840061926e+00],
     [3.57419329042577e-01 ,  7.15432086910138e+00 , -3.00729011800402e+00],
     [-8.87226591278481e-01 ,  7.05843630689226e+00 , -5.04470457518406e+00],
     [-6.76800637735018e-01 ,  2.81706845302599e+00 , -4.26080343845704e+00],
     [-3.07547680820838e+00 ,  5.23536678304774e+00 , -3.77719788744737e+00],
     [-3.44299762733077e+00 ,  3.88204044323898e+00 , -5.48089031040810e+00],
     [-4.88523014166804e+00 ,  3.79727130861959e+00 , -5.37301134034338e+00]],
    requires_grad=True, dtype=torch.float64)

# fitting_group = atom_positions
# main_group = atom_positions[[0, 1, 2, 3]]


def get_optimal_rotation(fitting_atoms: torch.tensor, reference_atoms: torch.tensor):
    fitting_atoms_centered = fitting_atoms - fitting_atoms.mean(dim=0)
    reference_atoms_centered = reference_atoms - reference_atoms.mean(dim=0)
    mat_R = torch.linalg.matmul(fitting_atoms_centered.T, reference_atoms_centered)
    F00 = mat_R[0][0] + mat_R[1][1] + mat_R[2][2]
    F01 = mat_R[1][2] - mat_R[2][1]
    F02 = mat_R[2][0] - mat_R[0][2]
    F03 = mat_R[0][1] - mat_R[1][0]
    F10 = F01
    F11 = mat_R[0][0] - mat_R[1][1] - mat_R[2][2]
    F12 = mat_R[0][1] + mat_R[1][0]
    F13 = mat_R[0][2] + mat_R[2][0]
    F20 = F02
    F21 = F12
    F22 = -mat_R[0][0] + mat_R[1][1] - mat_R[2][2]
    F23 = mat_R[1][2] + mat_R[2][1]
    F30 = F03
    F31 = F13
    F32 = F23
    F33 = -mat_R[0][0] - mat_R[1][1] + mat_R[2][2]
    row0 = torch.stack((F00, F01, F02, F03))
    row1 = torch.stack((F10, F11, F12, F13))
    row2 = torch.stack((F20, F21, F22, F23))
    row3 = torch.stack((F30, F31, F32, F33))
    F = torch.stack((row0, row1, row2, row3))
    w, v = torch.linalg.eigh(F)
    q = v[:, -1]
    R00 = q[0] * q[0] + q[1] * q[1] - q[2] * q[2] - q[3] * q[3]
    R01 = 2.0 * (q[1] * q[2] - q[0] * q[3])
    R02 = 2.0 * (q[1] * q[3] + q[0] * q[2])
    R10 = 2.0 * (q[1] * q[2] + q[0] * q[3])
    R11 = q[0] * q[0] - q[1] * q[1] + q[2] * q[2] - q[3] * q[3]
    R12 = 2.0 * (q[2] * q[3] - q[0] * q[1])
    R20 = 2.0 * (q[1] * q[3] - q[0] * q[2])
    R21 = 2.0 * (q[2] * q[3] + q[0] * q[1])
    R22 = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]
    row0 = torch.stack((R00, R01, R02))
    row1 = torch.stack((R10, R11, R12))
    row2 = torch.stack((R20, R21, R22))
    R = torch.stack((row0, row1, row2))
    return w, v, R


def test():
    # Using all atoms as the fitting group
    w, v, R = get_optimal_rotation(atom_positions, ref_atom_positions)
    main_group = atom_positions[[0, 1, 2, 3]] - atom_positions[[0, 1, 2, 3]].mean(dim=0)
    ref_main_group = ref_atom_positions[[0, 1, 2, 3]] - ref_atom_positions[[0, 1, 2, 3]].mean(dim=0)
    # Rotate the main group
    main_group_r = (R @ main_group.T).T
    w2, v2, R2 = get_optimal_rotation(ref_main_group, main_group_r)
    q = v2[:, -1]
    ref_quat = torch.tensor([1.0, 0.0, 0.0, 0.0], dtype=torch.float64)
    q_inner_ref_quat = torch.sum(ref_quat * q)
    if (q_inner_ref_quat > 0):
        final_q = q
    else:
        final_q = -1.0 * q
    print(final_q)
    # Force on q
    force = torch.tensor([0.0309292 , 1.74235 , 1.62984 , 1.67516], dtype=torch.float64)
    # Apply force
    (force[0] * final_q[0]).backward(retain_graph=True)
    (force[1] * final_q[1]).backward(retain_graph=True)
    (force[2] * final_q[2]).backward(retain_graph=True)
    (force[3] * final_q[3]).backward(retain_graph=True)
    print(atom_positions.grad)


if __name__ == '__main__':
    test()

I am not sure if this is the shortest way to fix #87. Anyway, if this approach works there are still a bunch of things to clean up. @giacomofiorin and @jhenin Any ideas?

@HanatoK
Copy link
Member Author

HanatoK commented Sep 12, 2024

It ought to be noted that even without considering the force on the fitting group, the force applying on the main group is wrong in the orientation component when the optimal alignment is enabled, because the old code just applies the force of the rotated frame directly to the original frame:

(*atoms)[ia].apply_force(FQ[i] * dq0_2[i]);

I think at least we have to rotate the force back to the original frame:

ag_rot = atoms->rot.inverse().matrix();
const auto f_ia = FQ[0] * dq0_2[0] + FQ[1] * dq0_2[1] + FQ[2] * dq0_2[2] + FQ[3] * dq0_2[3];
(*atoms)[ia].apply_force(ag_rot * f_ia);

@fhh2626 Maybe this is the reason why you found some scripted CVs based on orientation to be unstable.

@HanatoK HanatoK closed this Sep 12, 2024
@HanatoK HanatoK reopened this Sep 12, 2024
@HanatoK
Copy link
Member Author

HanatoK commented Sep 12, 2024

After more testing I conclude that the approach in this PR yields the correct result. I export the step 0 positions in 000_orientation-fitgroup_harmonic-ori-fixed and use pytorch to test (you can test it on Google Colab: https://colab.research.google.com/drive/1twhY_SPi3ccIu46Be21zGt0JbR-gqi6W?usp=sharing):

#!/usr/bin/env python3
import torch

torch.set_printoptions(precision=10)


# function to extract grad
def set_grad(var):
    def hook(grad):
        var.grad = grad
    return hook


main_group = torch.tensor(
    [[ 7.00603894, -0.47702751, -0.68594361],
     [ 6.1849732 , -0.13302646,  3.0389999 ],
     [ 5.2349905 ,  3.60400329,  2.87702477],
     [ 2.6549758 ,  3.21405895, -0.05499306],
     [ 1.11002766,  0.02001201,  1.68902446],
     [ 0.79599642,  1.8699884 ,  5.09001061],
     [-1.44499986,  4.62799966,  3.52899942],
     [-3.74298744,  1.86798795,  2.08600524],
     [-3.74097344,  0.09904095,  5.46300172],
     [-4.90700712,  3.24699298,  7.21803419]], requires_grad=True, dtype=torch.float64)

ref_main_group = torch.tensor(
    [[6.3061, -1.592, -3.585],
     [5.4031, -0.784, 0.045] ,
     [3.8061, 2.804, -0.129] ,
     [1.2441, 1.799, -2.726] ,
     [0.5861, -1.803, -1.516],
     [-0.2169, -0.926, 2.133],
     [-2.1679, 2.13, 1.258]  ,
     [-4.4689, 0.434, -1.263],
     [-4.8649, -2.25, 1.46]  ,
     [-5.6269, 0.188, 4.323]], dtype=torch.float64)

fitting_group = torch.tensor(
    [[ 8.42800577,  0.0420079 , -0.74403081],
     [ 7.00603894, -0.47702751, -0.68594361],
     [ 7.03995611, -2.00300543, -1.04193958],
     [ 6.232     , -0.252     ,  0.617     ],
     [ 5.034     , -0.207     ,  0.633     ],
     [ 6.91001276, -0.26600248,  1.77300131],
     [ 6.1849732 , -0.13302646,  3.0389999 ],
     [ 7.20098994, -0.31695137,  4.30201369],
     [ 5.3       ,  1.147     ,  3.253     ],
     [ 4.096     ,  1.082     ,  3.6       ],
     [ 5.87400947,  2.34899962,  2.93599796],
     [ 5.2349905 ,  3.60400329,  2.87702477],
     [ 6.31703081,  4.65098894,  2.4770467 ],
     [ 3.911     ,  3.676     ,  2.035     ],
     [ 2.936     ,  4.121     ,  2.584     ],
     [ 3.81797897,  3.24301039,  0.78301317],
     [ 2.6549758 ,  3.21405895, -0.05499306],
     [ 3.04500477,  2.74098549, -1.55998623],
     [ 1.656     ,  2.181     ,  0.554     ],
     [ 0.423     ,  2.412     ,  0.581     ],
     [ 2.00097516,  0.98201007,  1.06700298],
     [ 1.11002766,  0.02001201,  1.68902446],
     [ 2.01697998, -1.21900134,  2.02197084],
     [ 0.49      ,  0.565     ,  3.013     ],
     [-0.693     ,  0.401     ,  3.207     ],
     [ 1.25802926,  1.29900304,  3.82599577],
     [ 0.79599642,  1.8699884 ,  5.09001061],
     [ 2.074998  ,  2.57305516,  5.62095664],
     [-0.345     ,  2.92      ,  4.99      ],
     [-1.187     ,  3.009     ,  5.894     ],
     [-0.37699068,  3.70699752,  3.88398801],
     [-1.44499986,  4.62799966,  3.52899942],
     [-0.78001145,  5.37397741,  2.40196061],
     [-2.7       ,  3.899     ,  2.954     ],
     [-3.847     ,  4.392     ,  2.883     ],
     [-2.54397388,  2.62898609,  2.51399134],
     [-3.74298744,  1.86798795,  2.08600524],
     [-3.40099076,  0.78497807,  1.02698123],
     [-4.356     ,  1.13      ,  3.214     ],
     [-5.528     ,  0.96      ,  3.354     ],
     [-3.49404539,  0.63298789,  4.18601013],
     [-3.74097344,  0.09904095,  5.46300172],
     [-2.46600202, -0.44904453,  6.11800127],
     [-4.453     ,  1.029     ,  6.423     ],
     [-5.33      ,  0.583     ,  7.172     ],
     [-6.288     ,  3.553     ,  6.688     ],
     [-6.941     ,  4.323     ,  7.42      ],
     [-6.77997934,  3.20500922,  5.49202067],
     [-4.07700722,  2.28599541,  6.46500632],
     [-4.90700712,  3.24699298,  7.21803419],
     [-4.04901756,  4.48202806,  7.26299742]], requires_grad=True, dtype=torch.float64)

ref_fitting_group = torch.tensor(
    [[5.69469, -1.80418, -5.22382]    ,
     [6.37169, -1.08118, -4.10382]    ,
     [6.61169, 0.365824, -4.58082]    ,
     [5.62269, -1.09518, -2.77982]    ,
     [4.39969, -0.986176, -2.72882]   ,
     [6.36569, -1.21018, -1.66282]    ,
     [5.83469, -1.37018, -0.318824]   ,
     [7.00969, -1.45018, 0.678176]    ,
     [4.84269, -0.296176, 0.126176]   ,
     [3.77369, -0.596176, 0.648176]   ,
     [5.15669, 0.991824, -0.113824]   ,
     [4.27869, 2.10682, 0.190176]     ,
     [5.03669, 3.42282, -0.0748235]   ,
     [2.96669, 2.09282, -0.596824]    ,
     [1.89969, 2.39182, -0.0678235]   ,
     [3.01469, 1.69682, -1.88382]     ,
     [1.84869, 1.57082, -2.73482]     ,
     [2.30269, 1.40782, -4.19882]     ,
     [0.962686, 0.399824, -2.31982]   ,
     [-0.255314, 0.522824, -2.22882]  ,
     [1.57569, -0.758176, -2.00882]   ,
     [0.900686, -1.92218, -1.46882]   ,
     [1.91969, -3.07218, -1.33582]    ,
     [0.223686, -1.65518, -0.123824]  ,
     [-0.930314, -2.01818, 0.0891765] ,
     [0.918686, -0.964176, 0.800176]  ,
     [0.372686, -0.514176, 2.06618]   ,
     [1.49069, 0.164824, 2.88418]     ,
     [-0.814314, 0.435824, 1.92118]   ,
     [-1.84631, 0.257824, 2.56118]    ,
     [-0.707314, 1.45482, 1.04518]    ,
     [-1.78431, 2.37982, 0.745176]    ,
     [-1.24331, 3.48882, -0.178824]   ,
     [-3.00931, 1.72082, 0.109176]    ,
     [-4.14731, 2.02882, 0.455176]    ,
     [-2.80331, 0.769824, -0.821824]  ,
     [-3.86731, -0.00917647, -1.42282],
     [-3.28731, -0.820176, -2.59882]  ,
     [-4.57231, -0.946176, -0.440824] ,
     [-5.78931, -1.10018, -0.475824]  ,
     [-3.81431, -1.58718, 0.469176]   ,
     [-4.35131, -2.44718, 1.50418]    ,
     [-3.19331, -3.25818, 2.12018]    ,
     [-5.10031, -1.70118, 2.61018]    ,
     [-6.15131, -2.13818, 3.07218]    ,
     [-6.33131, 1.19882, 3.62918]     ,
     [-7.08531, 1.74482, 4.44018]     ,
     [-6.45431, 1.44382, 2.30618]     ,
     [-4.56431, -0.557176, 3.07518]   ,
     [-5.19131, 0.240824, 4.11318]    ,
     [-4.10131, 1.05882, 4.83518]], dtype=torch.float64)

# fitting_group = atom_positions
# main_group = atom_positions[[0, 1, 2, 3]]


def get_optimal_rotation(fitting_atoms: torch.tensor, reference_atoms: torch.tensor):
    fitting_atoms_centered = fitting_atoms - fitting_atoms.mean(dim=0)
    reference_atoms_centered = reference_atoms - reference_atoms.mean(dim=0)
    mat_R = torch.linalg.matmul(fitting_atoms_centered.T, reference_atoms_centered)
    F00 = mat_R[0][0] + mat_R[1][1] + mat_R[2][2]
    F01 = mat_R[1][2] - mat_R[2][1]
    F02 = mat_R[2][0] - mat_R[0][2]
    F03 = mat_R[0][1] - mat_R[1][0]
    F10 = F01
    F11 = mat_R[0][0] - mat_R[1][1] - mat_R[2][2]
    F12 = mat_R[0][1] + mat_R[1][0]
    F13 = mat_R[0][2] + mat_R[2][0]
    F20 = F02
    F21 = F12
    F22 = -mat_R[0][0] + mat_R[1][1] - mat_R[2][2]
    F23 = mat_R[1][2] + mat_R[2][1]
    F30 = F03
    F31 = F13
    F32 = F23
    F33 = -mat_R[0][0] - mat_R[1][1] + mat_R[2][2]
    row0 = torch.stack((F00, F01, F02, F03))
    row1 = torch.stack((F10, F11, F12, F13))
    row2 = torch.stack((F20, F21, F22, F23))
    row3 = torch.stack((F30, F31, F32, F33))
    F = torch.stack((row0, row1, row2, row3))
    w, v = torch.linalg.eigh(F)
    q = v[:, -1]
    R00 = q[0] * q[0] + q[1] * q[1] - q[2] * q[2] - q[3] * q[3]
    R01 = 2.0 * (q[1] * q[2] - q[0] * q[3])
    R02 = 2.0 * (q[1] * q[3] + q[0] * q[2])
    R10 = 2.0 * (q[1] * q[2] + q[0] * q[3])
    R11 = q[0] * q[0] - q[1] * q[1] + q[2] * q[2] - q[3] * q[3]
    R12 = 2.0 * (q[2] * q[3] - q[0] * q[1])
    R20 = 2.0 * (q[1] * q[3] - q[0] * q[2])
    R21 = 2.0 * (q[2] * q[3] + q[0] * q[1])
    R22 = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]
    row0 = torch.stack((R00, R01, R02))
    row1 = torch.stack((R10, R11, R12))
    row2 = torch.stack((R20, R21, R22))
    R = torch.stack((row0, row1, row2))
    return w, v, R


def test():
    # Using all atoms as the fitting group
    w, v, R = get_optimal_rotation(fitting_group, ref_fitting_group)
    main_group_centered = main_group - main_group.mean(dim=0)
    ref_main_group_centered = ref_main_group - ref_main_group.mean(dim=0)
    # Rotate the main group
    main_group_r = (R @ main_group_centered.T).T
    # main_group_r.register_hook(set_grad(main_group_r))
    w2, v2, R2 = get_optimal_rotation(ref_main_group_centered, main_group_r)
    q = v2[:, -1]
    ref_quat = torch.tensor([1.0, 0.0, 0.0, 0.0], dtype=torch.float64)
    q_inner_ref_quat = torch.sum(ref_quat * q)
    if (q_inner_ref_quat > 0):
        final_q = q
    else:
        final_q = -1.0 * q
    print('Orientation:\n', final_q)
    # Force on q
    force = torch.tensor([3.40697e-06, -8.24548e-05, -5.71708e-05, 5.95895e-05], dtype=torch.float64)
    # Apply force
    # torch.sum(force * final_q).backward()
    (force[0] * final_q[0]).backward(retain_graph=True)
    (force[1] * final_q[1]).backward(retain_graph=True)
    (force[2] * final_q[2]).backward(retain_graph=True)
    (force[3] * final_q[3]).backward(retain_graph=True)
    print('main group force:\n', main_group.grad)
    # print('main group (rotated) force:\n', main_group_r.grad)
    print('fitting group force:\n', fitting_group.grad)


if __name__ == '__main__':
    test()

Running the script above I get:

Orientation:
 tensor([ 0.9995740998,  0.0206194934,  0.0142967770, -0.0149015961],
       dtype=torch.float64, grad_fn=<MulBackward0>)
main group force:
 tensor([[ 1.1416871242e-06, -2.1675030111e-07,  2.1887093499e-06],
        [ 5.4482097062e-07,  2.3529714705e-06,  9.9506834760e-07],
        [-1.0401134101e-06,  1.2058529925e-06, -2.0142629441e-06],
        [-5.5701437658e-07, -1.9228780526e-06, -1.0270468392e-06],
        [ 8.8343701787e-07, -8.4768646263e-07,  1.7072966211e-06],
        [ 2.5990475762e-07,  1.7727919326e-06,  4.6161555350e-07],
        [-1.0699872250e-06, -5.4138277929e-08, -2.0460807878e-06],
        [-2.8601488120e-07, -2.9459041482e-06, -4.8797961308e-07],
        [ 6.8254335146e-07, -5.8989226354e-07,  1.3177494269e-06],
        [-5.5926332885e-07,  1.2456331103e-06, -1.0950691149e-06]],
       dtype=torch.float64)
fitting group force:
 tensor([[-2.3731612365e-07,  3.2301786591e-07, -4.9105157992e-07],
        [-1.6810540042e-07,  1.0706416237e-07, -3.4177118184e-07],
        [-5.8030434361e-08,  2.0503024511e-07, -1.2636040049e-07],
        [-1.4934347902e-07, -4.5224414081e-08, -2.9662940811e-07],
        [-1.3417062660e-07,  5.0258220450e-08, -2.7102443613e-07],
        [-1.4829614267e-07, -2.8974616434e-07, -2.8234144912e-07],
        [-1.4212171843e-07, -4.6731313271e-07, -2.6113091774e-07],
        [-1.4174803886e-07, -7.2704282873e-07, -2.4743289137e-07],
        [-4.5055920104e-08, -4.2717923225e-07, -6.8872138753e-08],
        [-5.7871920207e-08, -4.3185697595e-07, -9.4287841297e-08],
        [ 5.4739278171e-08, -3.7800592249e-07,  1.2839834016e-07],
        [ 1.5284363725e-07, -3.2335757854e-07,  3.2201192007e-07],
        [ 2.5251871673e-07, -3.0589798497e-07,  5.2062325475e-07],
        [ 1.4810340022e-07, -8.8871769492e-08,  3.0083367377e-07],
        [ 1.8384051561e-07, -7.7958831879e-08,  3.7181100774e-07],
        [ 9.9897761395e-08,  1.0357428178e-07,  1.9476329758e-07],
        [ 8.4618404140e-08,  3.3321092120e-07,  1.5273466458e-07],
        [ 5.1178690661e-08,  5.2736276664e-07,  7.6130600109e-08],
        [-8.6604643442e-10,  3.0618041065e-07, -1.6999452461e-08],
        [ 1.5911309030e-08,  3.9519520116e-07,  1.2139133843e-08],
        [-9.3597208672e-08,  1.7294218076e-07, -1.9594103125e-07],
        [-1.7795509330e-07,  1.0858739035e-07, -3.6155953182e-07],
        [-2.7414143400e-07, -2.9126579443e-08, -5.4719287206e-07],
        [-1.3652924080e-07, -4.5091170136e-08, -2.7099064201e-07],
        [-1.5787173139e-07,  5.2828749667e-09, -3.1621548572e-07],
        [-7.2418657507e-08, -2.3181834833e-07, -1.3337450362e-07],
        [-1.7773654783e-08, -3.7837871060e-07, -1.6704759833e-08],
        [ 4.2074739596e-08, -5.8315633988e-07,  1.1328136754e-07],
        [ 6.2864262516e-08, -2.3054332395e-07,  1.3730653151e-07],
        [ 6.1209299031e-08, -2.5385733174e-07,  1.3515685860e-07],
        [ 1.3398365669e-07, -6.9349707617e-08,  2.7160215451e-07],
        [ 2.1016485443e-07,  9.3727262669e-08,  4.1593416912e-07],
        [ 2.8594399356e-07,  2.2948711156e-07,  5.6082357222e-07],
        [ 1.5468869676e-07,  2.7852112580e-07,  2.9569468729e-07],
        [ 1.8922058617e-07,  3.2504308754e-07,  3.6248450376e-07],
        [ 6.5236863123e-08,  3.8490958658e-07,  1.1136826547e-07],
        [-2.7030824694e-10,  5.4742779563e-07, -2.7835830659e-08],
        [-8.3174957694e-08,  6.6649958856e-07, -1.9969162583e-07],
        [-6.0644762949e-08,  4.2065779410e-07, -1.4234374411e-07],
        [-6.7849378433e-08,  5.2210270257e-07, -1.6182055851e-07],
        [-1.0479874110e-07,  1.9345863559e-07, -2.1938185269e-07],
        [-1.5907125720e-07,  4.6486335021e-08, -3.2067053501e-07],
        [-2.2249333286e-07, -1.7100440280e-07, -4.3675436398e-07],
        [-8.1503104892e-08, -4.9222270716e-08, -1.6065967907e-07],
        [-1.0623324285e-07, -4.9566431860e-08, -2.1013547439e-07],
        [ 1.7156091066e-07, -3.0522919023e-08,  3.4487040720e-07],
        [ 2.2931461363e-07, -8.3881983142e-08,  4.6311462448e-07],
        [ 1.7558406204e-07,  1.9986852901e-07,  3.4143466365e-07],
        [ 1.4328785382e-08, -1.3607278975e-07,  3.5461121285e-08],
        [ 9.4692715701e-08, -2.2938789149e-07,  2.0094788713e-07],
        [ 1.6473220493e-07, -4.1246104006e-07,  3.5024748144e-07]],
       dtype=torch.float64)

Dumping the forces on the main group atoms shows:

main_group_forces:
[1.14169e-06, -2.16749e-07, 2.18871e-06]
[5.44821e-07, 2.35297e-06, 9.95068e-07]
[-1.04011e-06, 1.20585e-06, -2.01426e-06]
[-5.57014e-07, -1.92288e-06, -1.02705e-06]
[8.83437e-07, -8.47686e-07, 1.7073e-06]
[2.59905e-07, 1.77279e-06, 4.61615e-07]
[-1.06999e-06, -5.41385e-08, -2.04608e-06]
[-2.86015e-07, -2.9459e-06, -4.87979e-07]
[6.82543e-07, -5.89893e-07, 1.31775e-06]
[-5.59263e-07, 1.24563e-06, -1.09507e-06]

Fitting group forces:

fitting_group_forces:
[-2.37316e-07, 3.23018e-07, -4.91051e-07]
[-1.68105e-07, 1.07064e-07, -3.41771e-07]
[-5.80306e-08, 2.0503e-07, -1.26361e-07]
[-1.49343e-07, -4.52242e-08, -2.96629e-07]
[-1.34171e-07, 5.02583e-08, -2.71025e-07]
[-1.48296e-07, -2.89746e-07, -2.82341e-07]
[-1.42122e-07, -4.67313e-07, -2.6113e-07]
[-1.41748e-07, -7.27043e-07, -2.47432e-07]
[-4.50561e-08, -4.2718e-07, -6.88724e-08]
[-5.78721e-08, -4.31857e-07, -9.42881e-08]
[5.47391e-08, -3.78006e-07, 1.28398e-07]
[1.52844e-07, -3.23358e-07, 3.22012e-07]
[2.52519e-07, -3.05898e-07, 5.20623e-07]
[1.48104e-07, -8.8872e-08, 3.00834e-07]
[1.83841e-07, -7.7959e-08, 3.71811e-07]
[9.98979e-08, 1.03575e-07, 1.94763e-07]
[8.46185e-08, 3.33211e-07, 1.52735e-07]
[5.11788e-08, 5.27363e-07, 7.61307e-08]
[-8.66284e-10, 3.0618e-07, -1.69999e-08]
[1.59111e-08, 3.95195e-07, 1.21386e-08]
[-9.35974e-08, 1.72942e-07, -1.95941e-07]
[-1.77955e-07, 1.08587e-07, -3.61559e-07]
[-2.74141e-07, -2.91263e-08, -5.47192e-07]
[-1.36529e-07, -4.50918e-08, -2.7099e-07]
[-1.57872e-07, 5.28231e-09, -3.16215e-07]
[-7.24188e-08, -2.31819e-07, -1.33375e-07]
[-1.77739e-08, -3.78379e-07, -1.67052e-08]
[4.20745e-08, -5.83156e-07, 1.13281e-07]
[6.2864e-08, -2.30543e-07, 1.37306e-07]
[6.12091e-08, -2.53857e-07, 1.35156e-07]
[1.33984e-07, -6.93496e-08, 2.71602e-07]
[2.10165e-07, 9.3727e-08, 4.15934e-07]
[2.85944e-07, 2.29487e-07, 5.60824e-07]
[1.54689e-07, 2.78521e-07, 2.95695e-07]
[1.89221e-07, 3.25043e-07, 3.62485e-07]
[6.52367e-08, 3.84909e-07, 1.11368e-07]
[-2.705e-10, 5.47428e-07, -2.78363e-08]
[-8.31752e-08, 6.665e-07, -1.99692e-07]
[-6.06449e-08, 4.20657e-07, -1.42344e-07]
[-6.78492e-08, 5.22102e-07, -1.6182e-07]
[-1.04799e-07, 1.93458e-07, -2.19381e-07]
[-1.59071e-07, 4.64867e-08, -3.2067e-07]
[-2.22493e-07, -1.71004e-07, -4.36754e-07]
[-8.1503e-08, -4.92219e-08, -1.60659e-07]
[-1.06233e-07, -4.9566e-08, -2.10135e-07]
[1.71561e-07, -3.05224e-08, 3.4487e-07]
[2.29315e-07, -8.38815e-08, 4.63115e-07]
[1.75584e-07, 1.99869e-07, 3.41435e-07]
[1.43286e-08, -1.36072e-07, 3.54606e-08]
[9.46925e-08, -2.29387e-07, 2.00947e-07]
[1.64732e-07, -4.1246e-07, 3.50248e-07]

@HanatoK
Copy link
Member Author

HanatoK commented Sep 12, 2024

The latest commit adds the force calculation of the fitting group in cvm::atom_group::apply_force, which is mainly used by distance_dir, distance_vec and distance_pairs, so this will affect the test results as the old code does not taken the fitting group force into account.

It seems I still need to do the same for colvar::cartesian::apply_force, but I wonder if there is a unified and simplified way to do all of them...

@HanatoK
Copy link
Member Author

HanatoK commented Sep 13, 2024

The latest commit adds the force calculation of the fitting group in cvm::atom_group::apply_force, which is mainly used by distance_dir, distance_vec and distance_pairs, so this will affect the test results as the old code does not taken the fitting group force into account.

It seems I still need to do the same for colvar::cartesian::apply_force, but I wonder if there is a unified and simplified way to do all of them...

I think I have solved the issue in colvar::cartesian::apply_force. I don't expect the cartesian configuration in #570 (comment) can correctly handle the forces even on the main group without this PR.

@HanatoK HanatoK marked this pull request as ready for review September 13, 2024 15:31
@HanatoK HanatoK added the bugfix To be used only in PRs label Sep 13, 2024
@HanatoK
Copy link
Member Author

HanatoK commented Sep 13, 2024

Some tests like 000_orientation-fitgroup_harmonic-ori-moving are failed in GROMACS because they are generated with the wrong code.

@giacomofiorin
Copy link
Member

It ought to be noted that even without considering the force on the fitting group, the force applying on the main group is wrong in the orientation component when the optimal alignment is enabled, because the old code just applies the force of the rotated frame directly to the original frame:

I thought about it for some time, but I think you are right here and opened a PR containing your suggested changes (see #715), so that we can get the tests to run. (Somehow a configuration in your fork seems to prevent it?)

Prior to the orientation class, I had used a relative_orientation class to map the relative orientation of two homologous protein domains in the simulation frame. However, that came with the restriction that the two groups must be equal in size and have a similar structure to be fitted on one another. After adding the moving frame of reference the relative_orientation class became redundant, and under pressure to simplify the code for inclusion in NAMD we cut that feature. However, in doing so I forgot to add the necessary change in orientation 🤦

@fhh2626 Maybe this is the reason why you found some scripted CVs based on orientation to be unstable.

That seems at least possible. Besides you guys with Chris, Emad's and Mahmoud's labs have also used the specific combination of orientation in a rotated frame. I'll let the relevant people in each lab know.

giacomofiorin added a commit that referenced this pull request Sep 16, 2024
giacomofiorin added a commit that referenced this pull request Sep 17, 2024
giacomofiorin added a commit that referenced this pull request Sep 17, 2024
giacomofiorin added a commit that referenced this pull request Sep 17, 2024
giacomofiorin added a commit that referenced this pull request Sep 17, 2024
giacomofiorin added a commit that referenced this pull request Sep 17, 2024
@fhh2626
Copy link
Contributor

fhh2626 commented Sep 18, 2024

It ought to be noted that even without considering the force on the fitting group, the force applying on the main group is wrong in the orientation component when the optimal alignment is enabled, because the old code just applies the force of the rotated frame directly to the original frame:

(*atoms)[ia].apply_force(FQ[i] * dq0_2[i]);

I think at least we have to rotate the force back to the original frame:

ag_rot = atoms->rot.inverse().matrix();
const auto f_ia = FQ[0] * dq0_2[0] + FQ[1] * dq0_2[1] + FQ[2] * dq0_2[2] + FQ[3] * dq0_2[3];
(*atoms)[ia].apply_force(ag_rot * f_ia);

@fhh2626 Maybe this is the reason why you found some scripted CVs based on orientation to be unstable.

Aha, this takes me back to 2017. Yes, this is probably why Jerome finally implemented independent Euler angles.

@fhh2626
Copy link
Contributor

fhh2626 commented Sep 18, 2024

It ought to be noted that even without considering the force on the fitting group, the force applying on the main group is wrong in the orientation component when the optimal alignment is enabled, because the old code just applies the force of the rotated frame directly to the original frame:

(*atoms)[ia].apply_force(FQ[i] * dq0_2[i]);

I think at least we have to rotate the force back to the original frame:

ag_rot = atoms->rot.inverse().matrix();
const auto f_ia = FQ[0] * dq0_2[0] + FQ[1] * dq0_2[1] + FQ[2] * dq0_2[2] + FQ[3] * dq0_2[3];
(*atoms)[ia].apply_force(ag_rot * f_ia);

@fhh2626 Maybe this is the reason why you found some scripted CVs based on orientation to be unstable.

The problem was that the scripted colvars based on orientation never work. They are accurate during equilibration but the forces are wrong when combined with eABF and MtD.

@HanatoK
Copy link
Member Author

HanatoK commented Sep 23, 2024

In commit 8bca52f, I have adapted the above python script, which can read the forces on the quaternion, the positions of the main and fitting groups from the .colvars.traj file, calculate the forces on the main and fitting groups by pytorch, and then compare them with the Colvars forces from the debug log output. Running compare_with_pytorch.py should give the following result:

Compare AutoDiff/test.colvars.traj and AutoDiff/test.colvars.out
Frame 0: error of the main group forces  1.11817e-06 , fitting group forces  1.20083e-05
Frame 1: error of the main group forces  9.87591e-07 , fitting group forces  9.86903e-06
Frame 2: error of the main group forces  9.47259e-07 , fitting group forces  8.60061e-06
Frame 3: error of the main group forces  8.34791e-07 , fitting group forces  7.96560e-06
Frame 4: error of the main group forces  9.32297e-07 , fitting group forces  7.17781e-06
Frame 5: error of the main group forces  9.56846e-07 , fitting group forces  6.82099e-06
Frame 6: error of the main group forces  9.34533e-07 , fitting group forces  6.39845e-06
Frame 7: error of the main group forces  8.49146e-07 , fitting group forces  6.04901e-06
Frame 8: error of the main group forces  9.99861e-07 , fitting group forces  5.86383e-06
Frame 9: error of the main group forces  1.10474e-06 , fitting group forces  5.53244e-06
Frame 10: error of the main group forces  9.11126e-07 , fitting group forces  5.53287e-06
Frame 11: error of the main group forces  1.00340e-06 , fitting group forces  5.39484e-06
Frame 12: error of the main group forces  9.73327e-07 , fitting group forces  5.07908e-06
Frame 13: error of the main group forces  1.16297e-06 , fitting group forces  5.03956e-06
Frame 14: error of the main group forces  9.99171e-07 , fitting group forces  5.13602e-06
Frame 15: error of the main group forces  9.94178e-07 , fitting group forces  5.03783e-06
Frame 16: error of the main group forces  1.01119e-06 , fitting group forces  4.86784e-06
Frame 17: error of the main group forces  1.05533e-06 , fitting group forces  5.12652e-06
Frame 18: error of the main group forces  9.91700e-07 , fitting group forces  5.15495e-06
Frame 19: error of the main group forces  1.02495e-06 , fitting group forces  5.31620e-06
Frame 20: error of the main group forces  1.04892e-06 , fitting group forces  5.67836e-06
Compare AutoDiff/test.restart.colvars.traj and AutoDiff/test.restart.colvars.out
Frame 0: error of the main group forces  1.04892e-06 , fitting group forces  5.67836e-06
Frame 1: error of the main group forces  9.14922e-07 , fitting group forces  5.86772e-06
Frame 2: error of the main group forces  1.05013e-06 , fitting group forces  6.53281e-06
Frame 3: error of the main group forces  1.25815e-06 , fitting group forces  7.17189e-06
Frame 4: error of the main group forces  9.34113e-07 , fitting group forces  8.15678e-06
Frame 5: error of the main group forces  1.01390e-06 , fitting group forces  9.05391e-06
Frame 6: error of the main group forces  9.51627e-07 , fitting group forces  9.47496e-06
Frame 7: error of the main group forces  1.07709e-06 , fitting group forces  9.33779e-06
Frame 8: error of the main group forces  1.15006e-06 , fitting group forces  8.45700e-06
Frame 9: error of the main group forces  9.10178e-07 , fitting group forces  7.49064e-06
Frame 10: error of the main group forces  1.02086e-06 , fitting group forces  6.67798e-06
Frame 11: error of the main group forces  8.18973e-07 , fitting group forces  5.99307e-06
Frame 12: error of the main group forces  9.73185e-07 , fitting group forces  5.33572e-06
Frame 13: error of the main group forces  1.07389e-06 , fitting group forces  5.05486e-06
Frame 14: error of the main group forces  1.05703e-06 , fitting group forces  4.86863e-06
Frame 15: error of the main group forces  1.08636e-06 , fitting group forces  4.73282e-06
Frame 16: error of the main group forces  1.16335e-06 , fitting group forces  4.53803e-06
Frame 17: error of the main group forces  9.79813e-07 , fitting group forces  4.41468e-06
Frame 18: error of the main group forces  1.06675e-06 , fitting group forces  4.38263e-06
Frame 19: error of the main group forces  1.04698e-06 , fitting group forces  4.30725e-06
Frame 20: error of the main group forces  1.06430e-06 , fitting group forces  4.05200e-06

I have no idea about how to implement the debug gradients for forces, and it seems using pytorch is the most straightforward way to verify if the calculation is correct.

@HanatoK
Copy link
Member Author

HanatoK commented Sep 23, 2024

The test fails (see https://github.com/HanatoK/colvars/actions/runs/11002205050/job/30548634244#step:11:2287) because the max line length of spiff. To pass the test I think a patched version of spiff is required, and I have forked @jhenin's spiff fork to increase the limit (see HanatoK/spiff@287f343).

@giacomofiorin
Copy link
Member

The test fails (see https://github.com/HanatoK/colvars/actions/runs/11002205050/job/30548634244#step:11:2287) because the max line length of spiff. To pass the test I think a patched version of spiff is required, and I have forked @jhenin's spiff fork to increase the limit (see HanatoK/spiff@287f343).

@HanatoK I just pushed an updated version of the "CentOS9-devel" container, which contains spiff from your fork. Please get in touch with @jhenin to merge the changes, and consider adding the spiff repo under the Colvars org?

@HanatoK
Copy link
Member Author

HanatoK commented Sep 28, 2024

The test fails (see https://github.com/HanatoK/colvars/actions/runs/11002205050/job/30548634244#step:11:2287) because the max line length of spiff. To pass the test I think a patched version of spiff is required, and I have forked @jhenin's spiff fork to increase the limit (see HanatoK/spiff@287f343).

@HanatoK I just pushed an updated version of the "CentOS9-devel" container, which contains spiff from your fork. Please get in touch with @jhenin to merge the changes, and consider adding the spiff repo under the Colvars org?

The NAMD CI job uses CentOS 7 (https://github.com/HanatoK/colvars/actions/runs/11002205050/job/30801228083#step:1:44). How could I change it to CentOS9-devel?

I have made a PR to @jhenin's repo:
jhenin/spiff#1

@HanatoK
Copy link
Member Author

HanatoK commented Oct 11, 2024

The test data in 000_distancedir-fitgroup_harmonic-ddir-fixed-fit-forces/ includes an example of using distanceDir with fitting groups. Running compare_with_pytorch.py in 000_distancedir-fitgroup_harmonic-ddir-fixed-fit-forces/ shows:

Compare AutoDiff/test.colvars.traj and AutoDiff/test.colvars.out
Error of forces: group1 =  1.04362e-06, group2 =  1.04362e-06, fitting_group1 =  2.26990e-06, fitting_group2 =  2.18965e-06
Error of forces: group1 =  9.13646e-07, group2 =  9.13646e-07, fitting_group1 =  2.42821e-06, fitting_group2 =  2.45791e-06
Error of forces: group1 =  6.64942e-07, group2 =  6.64942e-07, fitting_group1 =  2.47079e-06, fitting_group2 =  3.28236e-06
Error of forces: group1 =  6.87113e-07, group2 =  6.87113e-07, fitting_group1 =  2.49371e-06, fitting_group2 =  1.76680e-05
Error of forces: group1 =  5.57825e-07, group2 =  5.57825e-07, fitting_group1 =  2.54335e-06, fitting_group2 =  3.75387e-06
Error of forces: group1 =  6.91201e-07, group2 =  6.91201e-07, fitting_group1 =  2.67590e-06, fitting_group2 =  2.64964e-06
Error of forces: group1 =  8.44771e-07, group2 =  8.44771e-07, fitting_group1 =  2.70702e-06, fitting_group2 =  2.14041e-06
Error of forces: group1 =  1.02005e-06, group2 =  1.02005e-06, fitting_group1 =  3.09716e-06, fitting_group2 =  2.16478e-06
Error of forces: group1 =  7.79908e-07, group2 =  7.79908e-07, fitting_group1 =  3.36727e-06, fitting_group2 =  2.00067e-06
Error of forces: group1 =  6.77613e-07, group2 =  6.77613e-07, fitting_group1 =  3.81188e-06, fitting_group2 =  1.95875e-06
Error of forces: group1 =  1.21103e-06, group2 =  1.21103e-06, fitting_group1 =  4.95870e-06, fitting_group2 =  1.89178e-06
Error of forces: group1 =  9.70569e-07, group2 =  9.70569e-07, fitting_group1 =  7.86909e-06, fitting_group2 =  1.82858e-06
Error of forces: group1 =  1.07966e-06, group2 =  1.07966e-06, fitting_group1 =  8.40452e-05, fitting_group2 =  1.82710e-06
Error of forces: group1 =  7.01130e-07, group2 =  7.01130e-07, fitting_group1 =  9.68549e-06, fitting_group2 =  1.71823e-06
Error of forces: group1 =  8.08735e-07, group2 =  8.08735e-07, fitting_group1 =  7.98778e-06, fitting_group2 =  1.72218e-06
Error of forces: group1 =  1.13511e-06, group2 =  1.13511e-06, fitting_group1 =  6.42386e-06, fitting_group2 =  1.84329e-06
Error of forces: group1 =  9.07320e-07, group2 =  9.07320e-07, fitting_group1 =  4.28382e-06, fitting_group2 =  1.65018e-06
Error of forces: group1 =  6.52166e-07, group2 =  6.52166e-07, fitting_group1 =  3.47928e-06, fitting_group2 =  1.78114e-06
Error of forces: group1 =  1.12802e-06, group2 =  1.12802e-06, fitting_group1 =  3.15942e-06, fitting_group2 =  1.73314e-06
Error of forces: group1 =  1.02333e-06, group2 =  1.02333e-06, fitting_group1 =  2.99396e-06, fitting_group2 =  1.66305e-06
Error of forces: group1 =  6.27694e-07, group2 =  6.27694e-07, fitting_group1 =  2.80540e-06, fitting_group2 =  1.74637e-06
Compare AutoDiff/test.restart.colvars.traj and AutoDiff/test.restart.colvars.out
Error of forces: group1 =  6.27694e-07, group2 =  6.27694e-07, fitting_group1 =  2.80540e-06, fitting_group2 =  1.74637e-06
Error of forces: group1 =  9.22526e-07, group2 =  9.22526e-07, fitting_group1 =  2.67639e-06, fitting_group2 =  1.66032e-06
Error of forces: group1 =  1.17957e-06, group2 =  1.17957e-06, fitting_group1 =  2.79768e-06, fitting_group2 =  1.79789e-06
Error of forces: group1 =  6.73760e-07, group2 =  6.73760e-07, fitting_group1 =  2.69680e-06, fitting_group2 =  1.73139e-06
Error of forces: group1 =  1.02055e-06, group2 =  1.02055e-06, fitting_group1 =  2.64771e-06, fitting_group2 =  1.88413e-06
Error of forces: group1 =  1.25014e-06, group2 =  1.25014e-06, fitting_group1 =  2.51141e-06, fitting_group2 =  1.80494e-06
Error of forces: group1 =  8.59313e-07, group2 =  8.59313e-07, fitting_group1 =  2.43197e-06, fitting_group2 =  1.68792e-06
Error of forces: group1 =  1.15801e-06, group2 =  1.15801e-06, fitting_group1 =  2.46410e-06, fitting_group2 =  1.63401e-06
Error of forces: group1 =  8.53782e-07, group2 =  8.53782e-07, fitting_group1 =  2.46379e-06, fitting_group2 =  1.79230e-06
Error of forces: group1 =  1.47441e-06, group2 =  1.47441e-06, fitting_group1 =  2.46259e-06, fitting_group2 =  1.61487e-06
Error of forces: group1 =  1.08762e-06, group2 =  1.08762e-06, fitting_group1 =  2.29627e-06, fitting_group2 =  1.85721e-06
Error of forces: group1 =  1.07532e-06, group2 =  1.07532e-06, fitting_group1 =  2.34095e-06, fitting_group2 =  1.72469e-06
Error of forces: group1 =  8.96383e-07, group2 =  8.96383e-07, fitting_group1 =  2.25299e-06, fitting_group2 =  1.55943e-06
Error of forces: group1 =  8.76761e-07, group2 =  8.76761e-07, fitting_group1 =  2.21469e-06, fitting_group2 =  1.83016e-06
Error of forces: group1 =  8.64423e-07, group2 =  8.64423e-07, fitting_group1 =  2.16633e-06, fitting_group2 =  1.77321e-06
Error of forces: group1 =  1.00183e-06, group2 =  1.00183e-06, fitting_group1 =  2.24684e-06, fitting_group2 =  1.69211e-06
Error of forces: group1 =  9.61221e-07, group2 =  9.61221e-07, fitting_group1 =  2.22402e-06, fitting_group2 =  1.61430e-06
Error of forces: group1 =  6.69440e-07, group2 =  6.69440e-07, fitting_group1 =  2.07205e-06, fitting_group2 =  1.74535e-06
Error of forces: group1 =  1.03060e-06, group2 =  1.03060e-06, fitting_group1 =  2.05437e-06, fitting_group2 =  1.68212e-06
Error of forces: group1 =  1.43741e-06, group2 =  1.43741e-06, fitting_group1 =  2.23114e-06, fitting_group2 =  1.77843e-06
Error of forces: group1 =  8.73627e-07, group2 =  8.73627e-07, fitting_group1 =  2.09917e-06, fitting_group2 =  1.81722e-06

@HanatoK HanatoK marked this pull request as ready for review October 11, 2024 21:23
We have already had the algorithm for computing the gradients of the
fitting group from the gradients of the main group. In cases that the
gradients of the main group are not available, we can "intercept" the
forces applied to the main group, and then use the same algorithm to
convert the forces applied to the main group with the rotation to the
forces applied to the fitting group.
This commit introduces the group_force_object that unifies the exertion
of atomic forces in an atom group. If a colvarcomp requires to apply
forces individually on atoms in an atom group, now it can do
```
auto ag_force = atoms->get_group_force_object();
for (size_t ia = 0; ia < atoms->size(); ia++) {
  const f_ia = ...;
  ag_force.set_atom_force(ia, f_ia);
}
```
If the optimal fitting is enabled for the atom group, group_force_object
will "intercept" the forces applied to the main group, and then calculate
the forces required on the fitting group by calling calc_fit_forces
internally, and then apply the forces on destruction.
This should better match what the existing apply_force does.
This commit dumps the Cartesian coordinates of each frame, which can be
read by the compare_with_pytorch.py. compare_with_pytorch.py then
calculates the forces on the main and the fitting groups by symbolic
gradients, and compares them with the Colvars debug output.
This commit aims to fix the incorrect optimization introduced in
commit adebdf6.
For the time being this field is only used for storing the main group
forces of non-scalable components with fitting groups.
Copy link
Member

@giacomofiorin giacomofiorin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks great to me.

@HanatoK
Copy link
Member Author

HanatoK commented Oct 12, 2024

Overall this looks good, with some questions to clarify.

As a general assessment, my preference would still be for adding the ability to compute the fit gradients explicitly, even if only for introspection purposes. That said, since you mentioned that BFEE now needs this feature, it makes sense to implement something that works specifically for the step of force application.

I agree with you that we should have some approaches to verify the force applied. However, I am not sure how to implement the real fit gradients (not the current propagating "fit forces" way) under the Colvars framework without some fundamental changes.

Here are my comments:

  • Fit gradients are very very useful also for introspection. I think that @jhenin may want to comment about what happens when you have enableFitGradients enabled, but these aren't actually available.

  • Are distanceVec and distanceDir using the new "force propagator" object? It would be best to make sure all non-scalar components covered for consistency.

And also the cartesian component. I think I should have cover all non-scalar components including those (i) applying forces individually on atoms of an atom group (such as orientation), and (ii) applying a force on the COM of a group (such as distanceDir).

  • Related to both points above, I would strongly consider deprecating enableFitGradients (in a separate PR).

Initially I want to do the same thing in this PR, but I wouldn't like to break too many tests, so I chose to only apply forces on the fitting group when enableFitGradients is on.

@HanatoK HanatoK changed the title [RFC] fix: try to solve #87 for non-scala components fix: try to solve #87 for non-scala components Oct 15, 2024
@jhenin jhenin merged commit 27a1b2e into Colvars:master Oct 15, 2024
13 checks passed
acmnpv pushed a commit to gromacs/gromacs that referenced this pull request Oct 25, 2024
All changes are limited to the copy of the library in `src/external/colvars`.

The following is a list of relevant pull requests (bugfixes only) in the library's repository:

- 728 Fix undefined behavior when getting the current working directory from std::filesystem Colvars/colvars#728 (@giacomofiorin)
- 724 Fix gradients and metric functions of distanceDir Colvars/colvars#724 (@giacomofiorin)
- 715 Add missing rotation in orientation component Colvars/colvars#715 (@giacomofiorin)
- 713 fix: try to solve #87 for non-scala components Colvars/colvars#713 (@HanatoK)
- 706 BUGFIX for Segmentation fault in colvarbias_meta::calc_energy() with useGrids off Colvars/colvars#706 (@alphataubio)
- 694 More robust condition to decide when biases run on thread 0 Colvars/colvars#694 (@giacomofiorin)
- 675 Fix initialization of histogram output files and move it to the right place Colvars/colvars#675 (@giacomofiorin)

Authors: @alphataubio, @giacomofiorin, @HanatoK
jhenin pushed a commit that referenced this pull request Dec 16, 2024
jhenin added a commit to Colvars/lammps that referenced this pull request Dec 16, 2024
- 759 min_image fix; Saves long runs from crashes;
  Colvars/colvars#759 (@PolyachenkoYA)

- 728 Fix undefined behavior when getting the current working directory from std::filesystem
  Colvars/colvars#728 (@giacomofiorin)

- 724 Fix gradients and metric functions of distanceDir
  Colvars/colvars#724 (@giacomofiorin)

- 715 Add missing rotation in orientation component
  Colvars/colvars#715 (@giacomofiorin)

- 713 fix: try to solve lammps#87 for non-scala components
  Colvars/colvars#713 (@HanatoK)

- 706 BUGFIX for Segmentation fault in colvarbias_meta::calc_energy() with useGrids off
  Colvars/colvars#706 (@alphataubio)

- 701 Do not try accessing LAMMPS proxy object before allocating it
  Colvars/colvars#701 (@giacomofiorin)

Authors: @alphataubio, @giacomofiorin, @HanatoK, @PolyachenkoYA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix To be used only in PRs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement fit gradients for non-scalar components (see also #61)
4 participants