Bioplib
Protein Structure C Library
 All Data Structures Files Functions Variables Typedefs Macros Pages
fit.c
Go to the documentation of this file.
1 /************************************************************************/
2 /**
3 
4  \file fit.c
5 
6  \version V1.7
7  \date 17.07.14
8  \brief Perform least squares fitting of coordinate sets
9 
10  \copyright (c) UCL / Dr. Andrew C. R. Martin 1993-2014
11  \author Dr. Andrew C. R. Martin
12  \par
13  Institute of Structural & Molecular Biology,
14  University College London,
15  Gower Street,
16  London.
17  WC1E 6BT.
18  \par
19  andrew@bioinf.org.uk
20  andrew.martin@ucl.ac.uk
21 
22 **************************************************************************
23 
24  This code is NOT IN THE PUBLIC DOMAIN, but it may be copied
25  according to the conditions laid out in the accompanying file
26  COPYING.DOC.
27 
28  The code may be modified as required, but any modifications must be
29  documented so that the person responsible can be identified.
30 
31  The code may not be sold commercially or included as part of a
32  commercial product except as described in the file COPYING.DOC.
33 
34 **************************************************************************
35 
36  Description:
37  ============
38 
39  This code performs least squares fitting of two coordinate set using
40  the method of McLachlan as modified by Sutcliffe.
41 
42 **************************************************************************
43 
44  Usage:
45  ======
46  Passed two coordinate arrays both centred around the origin and,
47  optionally, an array of weights, returns a rotation matrix.
48 
49 **************************************************************************
50 
51  Revision History:
52  =================
53 - V1.0 04.02.91 Original
54 - V1.1 01.06.92 ANSIed and static'd
55 - V1.2 08.12.92 Changed abs() to ABS() using macros.h. Includes stdio.h
56 - V1.3 11.02.94 Changed column flag to BOOL
57 - V1.4 03.06.97 Corrected documentation
58 - V1.5 03.04.09 Initialize clep in qikfit() By: CTP
59 - V1.6 07.07.14 Use bl prefix for functions By: CTP
60 - V1.7 17.07.14 Removed unused varables By: ACRM
61 
62 *************************************************************************/
63 /* Doxygen
64  -------
65  #GROUP Coordinate Fitting
66  #SUBGROUP Fitting based on coordinate arrays
67  #FUNCTION blMatfit()
68  Fit coordinate array x2 to x1 both centred around the origin and of
69  length n. Optionally weighted with the wt1 array if wt1 is not NULL.
70  If column is set the matrix will be returned column-wise rather
71  than row-wise.
72 */
73 /************************************************************************/
74 /* Includes
75 */
76 #include <math.h>
77 #include <stdio.h>
78 
79 #include "MathType.h"
80 #include "fit.h"
81 #include "macros.h"
82 
83 /************************************************************************/
84 /* Defines and macros
85 */
86 #define SMALL 1.0e-20 /* Convergence cutoffs */
87 #define SMALSN 1.0e-10
88 
89 /************************************************************************/
90 /* Globals
91 */
92 
93 /************************************************************************/
94 /* Prototypes
95 */
96 static void qikfit(REAL umat[3][3], REAL rm[3][3], BOOL column);
97 
98 /************************************************************************/
99 /*>BOOL blMatfit(COOR *x1, COOR *x2, REAL rm[3][3], int n,
100  REAL *wt1, BOOL column)
101  -------------------------------------------------------
102 *//**
103 
104  \param[in] *x1 First (fixed) array of coordinates
105  \param[in] *x2 Second (mobile) array of coordinates
106  \param[in] n Number of coordinates
107  \param[in] *wt1 Weight array or NULL
108  \param[in] column TRUE: Output a column-wise matrix (as used
109  by FRODO)
110  FALSE: Output a standard row-wise matrix.
111  \param[out] rm Returned rotation matrix
112  \return TRUE: success
113  FALSE: error
114 
115  Fit coordinate array x2 to x1 both centred around the origin and of
116  length n. Optionally weighted with the wt1 array if wt1 is not NULL.
117  If column is set the matrix will be returned column-wise rather
118  than row-wise.
119 
120 - 04.02.91 Original based on code by Mike Sutcliffe
121 - 01.06.92 ANSIed & doc'd
122 - 17.06.93 various changes for release (including parameters)
123 - 11.03.94 column changed to BOOL
124 - 25.11.02 Corrected header!
125 - 07.07.14 Use bl prefix for functions By: CTP
126 
127 */
128 BOOL blMatfit(COOR *x1, /* First coord array */
129  COOR *x2, /* Second coord array */
130  REAL rm[3][3], /* Rotation matrix */
131  int n, /* Number of points */
132  REAL *wt1, /* Weight array */
133  BOOL column) /* Column-wise output */
134 {
135  int i,j;
136  REAL umat[3][3];
137 
138 
139  if(n<2)
140  {
141  return(FALSE);
142  }
143 
144  if(wt1)
145  {
146  for(i=0;i<3;i++)
147  {
148  for(j=0;j<3;j++) umat[i][j] = 0.0;
149 
150  for(j=0;j<n;j++)
151  {
152  switch(i)
153  {
154  case 0:
155  umat[i][0] += wt1[j] * x1[j].x * x2[j].x;
156  umat[i][1] += wt1[j] * x1[j].x * x2[j].y;
157  umat[i][2] += wt1[j] * x1[j].x * x2[j].z;
158  break;
159  case 1:
160  umat[i][0] += wt1[j] * x1[j].y * x2[j].x;
161  umat[i][1] += wt1[j] * x1[j].y * x2[j].y;
162  umat[i][2] += wt1[j] * x1[j].y * x2[j].z;
163  break;
164  case 2:
165  umat[i][0] += wt1[j] * x1[j].z * x2[j].x;
166  umat[i][1] += wt1[j] * x1[j].z * x2[j].y;
167  umat[i][2] += wt1[j] * x1[j].z * x2[j].z;
168  break;
169  }
170  }
171  }
172  }
173  else
174  {
175  for(i=0;i<3;i++)
176  {
177  for(j=0;j<3;j++) umat[i][j] = 0.0;
178 
179  for(j=0;j<n;j++)
180  {
181  switch(i)
182  {
183  case 0:
184  umat[i][0] += x1[j].x * x2[j].x;
185  umat[i][1] += x1[j].x * x2[j].y;
186  umat[i][2] += x1[j].x * x2[j].z;
187  break;
188  case 1:
189  umat[i][0] += x1[j].y * x2[j].x;
190  umat[i][1] += x1[j].y * x2[j].y;
191  umat[i][2] += x1[j].y * x2[j].z;
192  break;
193  case 2:
194  umat[i][0] += x1[j].z * x2[j].x;
195  umat[i][1] += x1[j].z * x2[j].y;
196  umat[i][2] += x1[j].z * x2[j].z;
197  break;
198  }
199  }
200  }
201  }
202  qikfit(umat,rm,column);
203 
204  return(TRUE);
205 }
206 
207 /************************************************************************/
208 /*>static void qikfit(REAL umat[3][3], REAL rm[3][3], BOOL column)
209  ---------------------------------------------------------------
210 *//**
211 
212  \param[in] umat The U matrix
213  \param[in] column TRUE: Create a column-wise matrix
214  (other way round from normal).
215  \param[out] rm The output rotation matrix
216 
217  Does the actual fitting for matfit().
218 - 04.02.91 Original based on code by Mike Sutcliffe
219 - 01.06.92 ANSIed & doc'd
220 - 11.03.94 column changed to BOOL
221 - 03.04.09 Initialize clep for fussy compliers. By: CTP
222 - 17.07.14 Removed unused variables By: ACRM
223 */
224 static void qikfit(REAL umat[3][3],
225  REAL rm[3][3],
226  BOOL column)
227 {
228 
229  REAL rot[3][3],
230  turmat[3][3],
231  c[3][3],
232  coup[3],
233  dir[3],
234  step[3],
235  v[3],
236  rtsum,rtsump,
237  stp,stcoup,
238  ud,tr,ta,cs,sn,ac,
239  delta,
240  gfac,
241  cle,
242  clep = 0.0;
243  int i,j,k,l,m,
244  jmax,
245  ncyc,
246  nsteep,
247  nrem;
248 
249  /* Rotate repeatedly to reduce couple about initial direction to zero.
250  Clear the rotation matrix
251  */
252  for(l=0;l<3;l++)
253  {
254  for(m=0;m<3;m++)
255  rot[l][m] = 0.0;
256  rot[l][l] = 1.0;
257  }
258 
259  /* Copy vmat[][] (sp) into umat[][] (dp) */
260  jmax = 30;
261  rtsum = umat[0][0] + umat[1][1] + umat[2][2];
262  delta = 0.0;
263 
264  for(ncyc=0;ncyc<jmax;ncyc++)
265  {
266  /* Modified CG. For first and every NSTEEP cycles, set previous
267  step as zero and do an SD step
268  */
269  nsteep = 3;
270  nrem = ncyc-nsteep*(int)(ncyc/nsteep);
271 
272  if(!nrem)
273  {
274  for(i=0;i<3;i++) step[i]=0.0;
275  clep = 1.0;
276  }
277 
278  /* Couple */
279  coup[0] = umat[1][2]-umat[2][1];
280  coup[1] = umat[2][0]-umat[0][2];
281  coup[2] = umat[0][1]-umat[1][0];
282  cle = sqrt(coup[0]*coup[0] + coup[1]*coup[1] + coup[2]*coup[2]);
283 
284  /* Gradient vector is now -coup */
285  gfac = (cle/clep)*(cle/clep);
286 
287  /* Value of rtsum from previous step */
288  rtsump = rtsum;
289  clep = cle;
290  if(cle < SMALL) break;
291 
292  /* Step vector conjugate to previous */
293  stp = 0.0;
294  for(i=0;i<3;i++)
295  {
296  step[i]=coup[i]+step[i]*gfac;
297  stp += (step[i] * step[i]);
298  }
299  stp = 1.0/sqrt(stp);
300 
301  /* Normalised step */
302  for(i=0;i<3;i++) dir[i] = stp*step[i];
303 
304  /* Couple resolved along step direction */
305  stcoup = coup[0]*dir[0] + coup[1]*dir[1] + coup[2]*dir[2];
306 
307  /* Component of UMAT along direction */
308  ud = 0.0;
309  for(l=0;l<3;l++)
310  for(m=0;m<3;m++)
311  ud += umat[l][m]*dir[l]*dir[m];
312 
313 
314  tr = umat[0][0]+umat[1][1]+umat[2][2]-ud;
315  ta = sqrt(tr*tr + stcoup*stcoup);
316  cs=tr/ta;
317  sn=stcoup/ta;
318 
319  /* If cs<0 then posiiton is unstable, so don't stop */
320  if((cs>0.0) && (ABS(sn)<SMALSN)) break;
321 
322  /* Turn matrix for correcting rotation:
323 
324  Symmetric part
325  */
326  ac = 1.0-cs;
327  for(l=0;l<3;l++)
328  {
329  v[l] = ac*dir[l];
330  for(m=0;m<3;m++)
331  turmat[l][m] = v[l]*dir[m];
332  turmat[l][l] += cs;
333  v[l]=dir[l]*sn;
334  }
335 
336  /* Asymmetric part */
337  turmat[0][1] -= v[2];
338  turmat[1][2] -= v[0];
339  turmat[2][0] -= v[1];
340  turmat[1][0] += v[2];
341  turmat[2][1] += v[0];
342  turmat[0][2] += v[1];
343 
344  /* Update total rotation matrix */
345  for(l=0;l<3;l++)
346  {
347  for(m=0;m<3;m++)
348  {
349  c[l][m] = 0.0;
350  for(k=0;k<3;k++)
351  c[l][m] += turmat[l][k]*rot[k][m];
352  }
353  }
354 
355  for(l=0;l<3;l++)
356  for(m=0;m<3;m++)
357  rot[l][m] = c[l][m];
358 
359  /* Update umat tensor */
360  for(l=0;l<3;l++)
361  for(m=0;m<3;m++)
362  {
363  c[l][m] = 0.0;
364  for(k=0;k<3;k++)
365  c[l][m] += turmat[l][k]*umat[k][m];
366  }
367 
368  for(l=0;l<3;l++)
369  for(m=0;m<3;m++)
370  umat[l][m] = c[l][m];
371 
372  rtsum = umat[0][0] + umat[1][1] + umat[2][2];
373  delta = rtsum - rtsump;
374 
375  /* If no improvement in this cycle then stop */
376  if(ABS(delta)<SMALL) break;
377 
378  /* Next cycle */
379  }
380 
381  /* Copy rotation matrix for output */
382  if(column)
383  {
384  for(i=0;i<3;i++)
385  for(j=0;j<3;j++)
386  rm[j][i] = rot[i][j];
387  }
388  else
389  {
390  for(i=0;i<3;i++)
391  for(j=0;j<3;j++)
392  rm[i][j] = rot[i][j];
393  }
394 }
REAL x
Definition: MathType.h:70
#define SMALL
Definition: fit.c:86
short BOOL
Definition: SysDefs.h:64
#define SMALSN
Definition: fit.c:87
#define FALSE
Definition: macros.h:223
Useful macros.
Definition: MathType.h:69
double REAL
Definition: MathType.h:67
#define TRUE
Definition: macros.h:219
BOOL blMatfit(COOR *x1, COOR *x2, REAL rm[3][3], int n, REAL *wt1, BOOL column)
Definition: fit.c:128
Include file for least squares fitting.
#define ABS(x)
Definition: macros.h:235
Type definitions for maths.
REAL z
Definition: MathType.h:70
REAL y
Definition: MathType.h:70