4444del _platform
4545
4646
47- def modify (system , k_hard = 100 , k_soft = 5 , optimise_angles = True ):
47+ def modify (system , k_hard = 100 , k_soft = 5 , optimise_angles = True , num_optimise = 10 ):
4848 """
4949 Apply Boresch modifications to ghost atom bonded terms to avoid non-physical
5050 coupling between the ghost atoms and the physical region.
@@ -67,6 +67,11 @@ def modify(system, k_hard=100, k_soft=5, optimise_angles=True):
6767 Whether to optimise the equilibrium value of the angle terms involving
6868 ghost atoms for non-planar triple junctions.
6969
70+ num_optimise : int, optional
71+ The number of repeats to average over when optimising the equilibrium
72+ value of the angle terms involving ghost atoms for non-planar triple
73+ junctions.
74+
7075 Returns
7176 -------
7277
@@ -217,6 +222,7 @@ def modify(system, k_hard=100, k_soft=5, optimise_angles=True):
217222 k_hard = k_hard ,
218223 k_soft = k_soft ,
219224 optimise_angles = optimise_angles ,
225+ num_optimise = num_optimise ,
220226 )
221227
222228 # Higher order junction.
@@ -229,6 +235,7 @@ def modify(system, k_hard=100, k_soft=5, optimise_angles=True):
229235 k_hard = k_hard ,
230236 k_soft = k_soft ,
231237 optimise_angles = optimise_angles ,
238+ num_optimise = num_optimise ,
232239 )
233240
234241 # Now lambda = 1.
@@ -252,6 +259,7 @@ def modify(system, k_hard=100, k_soft=5, optimise_angles=True):
252259 k_hard = k_hard ,
253260 k_soft = k_soft ,
254261 optimise_angles = optimise_angles ,
262+ num_optimise = num_optimise ,
255263 is_lambda1 = True ,
256264 )
257265
@@ -265,6 +273,7 @@ def modify(system, k_hard=100, k_soft=5, optimise_angles=True):
265273 k_hard = k_hard ,
266274 k_soft = k_soft ,
267275 optimise_angles = optimise_angles ,
276+ num_optimise = num_optimise ,
268277 is_lambda1 = True ,
269278 )
270279
@@ -597,6 +606,7 @@ def _triple(
597606 k_hard = 100 ,
598607 k_soft = 5 ,
599608 optimise_angles = True ,
609+ num_optimise = 10 ,
600610 is_lambda1 = False ,
601611):
602612 r"""
@@ -639,6 +649,10 @@ def _triple(
639649 Whether to optimise the equilibrium value of the angle terms involving
640650 ghost atoms for non-planar triple junctions.
641651
652+ num_optimise : int, optional
653+ The number of repeats to use when optimising the angle terms involving
654+ ghost atoms for non-planar triple junctions.
655+
642656 is_lambda1 : bool, optional
643657 Whether the junction is at lambda = 1.
644658
@@ -870,32 +884,47 @@ def _triple(
870884 .commit ()
871885 )
872886
873- # Optimise the equilibrium value of theta0 for the softened angle term .
887+ # Optimise the equilibrium value of theta0 for the softened angle terms .
874888 if optimise_angles :
875889 _logger .debug (" Optimising equilibrium values for softened angles." )
876890
877891 import sire .morph as _morph
878892 from sire .units import radian as _radian
879893
880- # Minimise the molecule.
881- min_mol = _morph .link_to_reference (mol )
882- minimiser = min_mol .minimisation (
883- lambda_value = 1.0 if is_lambda1 else 0.0 ,
884- constraint = "none" ,
885- platform = "cpu" ,
886- )
887- minimiser .run ()
888-
889- # Commit the changes.
890- min_mol = minimiser .commit ()
891-
892- # Get the equilibrium angle values.
894+ # Initialise the equilibrium angle values.
893895 theta0s = {}
894896 for idx in angle_idxs :
895- try :
896- theta0s [idx ] = min_mol .angles (* idx ).sizes ()[0 ].to (_radian )
897- except :
898- raise ValueError (f"Could not find optimised angle term: { idx } " )
897+ theta0s [idx ] = []
898+
899+ # Perform multiple minimisations to get an average for the theta0 values.
900+ for _ in range (num_optimise ):
901+ # Minimise the molecule.
902+ min_mol = _morph .link_to_reference (mol )
903+ minimiser = min_mol .minimisation (
904+ lambda_value = 1.0 if is_lambda1 else 0.0 ,
905+ constraint = "none" ,
906+ platform = "cpu" ,
907+ )
908+ minimiser .run ()
909+
910+ # Commit the changes.
911+ min_mol = minimiser .commit ()
912+
913+ # Get the equilibrium angle values.
914+ for idx in angle_idxs :
915+ try :
916+ theta0s [idx ].append (min_mol .angles (* idx ).sizes ()[0 ].to (_radian ))
917+ except :
918+ raise ValueError (f"Could not find optimised angle term: { idx } " )
919+
920+ # Compute the mean and standard error.
921+ import numpy as _np
922+
923+ theta0_means = {}
924+ theta0_stds = {}
925+ for idx in theta0s :
926+ theta0_means [idx ] = _np .mean (theta0s [idx ])
927+ theta0_stds [idx ] = _np .std (theta0s [idx ]) / _np .sqrt (len (theta0s [idx ]))
899928
900929 # Get the existing angles.
901930 angles = mol .property ("angle" + suffix )
@@ -913,7 +942,8 @@ def _triple(
913942 # This is the softened angle term.
914943 if idx in angle_idxs :
915944 # Get the optimised equilibrium angle.
916- theta0 = theta0s [idx ]
945+ theta0 = theta0_means [idx ]
946+ std = theta0_stds [idx ]
917947
918948 # Create the new angle function.
919949 amber_angle = _SireMM .AmberAngle (k_soft , theta0 )
@@ -926,7 +956,7 @@ def _triple(
926956
927957 _logger .debug (
928958 f" Optimising angle: [{ idx0 .value ()} -{ idx1 .value ()} -{ idx2 .value ()} ], "
929- f"{ p .function ()} --> { expression } "
959+ f"{ p .function ()} --> { expression } (std err: { std :.3f } radian) "
930960 )
931961
932962 else :
@@ -952,6 +982,7 @@ def _higher(
952982 k_hard = 100 ,
953983 k_soft = 5 ,
954984 optimise_angles = True ,
985+ num_optimise = 10 ,
955986 is_lambda1 = False ,
956987):
957988 r"""
@@ -984,6 +1015,10 @@ def _higher(
9841015 Whether to optimise the equilibrium value of the angle terms involving
9851016 ghost atoms for non-planar triple junctions.
9861017
1018+ num_optimise : int, optional
1019+ The number of repeats to use when optimising the angle terms involving
1020+ ghost atoms for non-planar triple junctions.
1021+
9871022 is_lambda1 : bool, optional
9881023 Whether the junction is at lambda = 1.
9891024
@@ -1072,6 +1107,8 @@ def _higher(
10721107 k_hard = k_hard ,
10731108 k_soft = k_soft ,
10741109 optimise_angles = optimise_angles ,
1110+ num_optimise = num_optimise ,
1111+ is_lambda1 = is_lambda1 ,
10751112 )
10761113
10771114
0 commit comments