view spring_package/pulchra/pulchra.c @ 21:5feab7f00f02 draft

"planemo upload commit b0ede77caf410ab69043d33a44e190054024d340-dirty"
author guerler
date Wed, 28 Oct 2020 05:34:36 +0000
parents c790d25086dc
children
line wrap: on
line source


//
// PULCHRA
// Protein Chain Restoration Algorithm
//
// Version 3.04
// July 2007
// Contact: Piotr Rotkiewicz, piotr -at- pirx -dot- com
//
// to compile:
// cc -O3 pulchra.c pulchra_data.c -lm -o pulchra
//
// to use:
// ./pulchra file.pdb
//
// to display available options:
// ./pulchra
//

#define COMPILE_BB
#define COMPILE_ROT

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/timeb.h>
#include <time.h>

#define uchar unsigned char
#define uint unsigned int
#define real double

#include "pulchra_common.h"

#define PULCHRA_VERSION 3.04
#define MAX_BUF_SIZE 1000

#define FILE_SUCCESS     0
#define FILE_NOT_FOUND  -1
#define FILE_WARNING    -2

#define FATAL_MAE -1

#define FLAG_BACKBONE  1
#define FLAG_CALPHA    2
#define FLAG_SIDECHAIN 4
#define FLAG_SCM       8

#define FLAG_PROTEIN  1
#define FLAG_DNA      2
#define FLAG_RNA      4
#define FLAG_CHYDRO   8

#define RADDEG 180./M_PI
#define DEGRAD M_PI/180.

int _VERBOSE = 0;
int _BB_REARRANGE = 1;
int _BB_OPTIMIZE = 0;
int _CA_OPTIMIZE = 1;
int _CA_RANDOM = 0;
int _CA_ITER = 100;
int _CA_TRAJECTORY = 0;
int _CISPRO = 0;
int _CHIRAL = 1;
int _CENTER_CHAIN = 0;
int _REBUILD_BB = 1;
int _REBUILD_SC = 1;
int _REBUILD_H = 0;
int _PDB_SG = 0;
int _TIME_SEED = 0;
int _XVOLUME = 1;
int _XVOL_ITER = 3;
real _CA_START_DIST = 3.0;
real _CA_XVOL_DIST = 3.5;
real _SG_XVOL_DIST = 1.6;

#define CALC_C_ALPHA
#define CALC_C_ALPHA_ANGLES
#define CALC_C_ALPHA_START
#define CALC_C_ALPHA_XVOL

real CA_K=10.0;
real CA_ANGLE_K=20.0;
real CA_START_K=0.01;
real CA_XVOL_K=10.00;

#define CA_DIST 3.8
#define CA_DIST_TOL 0.1
#define CA_DIST_CISPRO 2.9
#define CA_DIST_CISPRO_TOL 0.1
#define E_EPS 1e-10

// grid resolution (used for fast clash detection)
#define GRID_RES 6.0

int chain_length = 0;

char AA_NAMES[21][4] =
  { "GLY", "ALA", "SER", "CYS", "VAL",
    "THR", "ILE", "PRO", "MET", "ASP",
    "ASN", "LEU", "LYS", "GLU", "GLN",
    "ARG", "HIS", "PHE", "TYR", "TRP",
    "UNK" };

char SHORT_AA_NAMES[22] = { "GASCVTIPMDNLKEQRHFYWX" };

int AA_NUMS[256];

int nheavy[20] = { 0, 1, 2, 2, 3, 3, 4, 3, 4, 4, 4, 4, 5, 5, 5, 7, 6, 7, 8, 10};

char *backbone_atoms[4] = { "N  ", "CA ", "C  ", "O  " };

char *heavy_atoms[200]= {
/* GLY */  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* ALA */ "CB ", NULL,   NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* SER */ "CB ", "OG ",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* CYS */ "CB ", "SG ",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* VAL */ "CB ", "CG1", "CG2",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* THR */ "CB ", "OG1", "CG2",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* ILE */ "CB ", "CG1", "CG2", "CD1",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* PRO */ "CB ", "CG ", "CD ",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* MET */ "CB ", "CG ", "SD ", "CE ",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* ASP */ "CB ", "CG ", "OD1", "OD2",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* ASN */ "CB ", "CG ", "OD1", "ND2",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* LEU */ "CB ", "CG ", "CD1", "CD2",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
/* LYS */ "CB ", "CG ", "CD ", "CE ", "NZ ",  NULL,  NULL,  NULL,  NULL,  NULL,
/* GLU */ "CB ", "CG ", "CD ", "OE1", "OE2",  NULL,  NULL,  NULL,  NULL,  NULL,
/* GLN */ "CB ", "CG ", "CD ", "OE1", "NE2",  NULL,  NULL,  NULL,  NULL,  NULL,
/* ARG */ "CB ", "CG ", "CD ", "NE ", "CZ ", "NH1", "NH2",  NULL,  NULL,  NULL,
/* HIS */ "CB ", "CG ", "ND1", "CD2", "CE1", "NE2",  NULL,  NULL,  NULL,  NULL,
/* PHE */ "CB ", "CG ", "CD1", "CD2", "CE1", "CE2", "CZ ",  NULL,  NULL,  NULL,
/* TYR */ "CB ", "CG ", "CD1", "CD2", "CE1", "CE2", "CZ ", "OH ",  NULL,  NULL,
/* TRP */ "CB ", "CG ", "CD1", "CD2", "NE1", "CE2", "CE3", "CZ2", "CZ3", "CH2"};

/* reads full-atom pdb file */

struct _res_type;

typedef struct _atom_type {
  struct _atom_type *next;
  real x, y, z;
  char *name;
  int num, locnum;
  int flag;
  char cispro;
  int gx, gy, gz;
  struct _res_type *res;
  struct _atom_type *prev;
} atom_type;

typedef struct _res_type {
  struct _res_type *next;
  atom_type *atoms;
  int num, locnum, natoms;
  int type;
  char pdbsg;
  char protein;
  char *name;
  char chain;
  real sgx, sgy, sgz;
  real cmx, cmy, cmz;
  struct _res_type *prev;
} res_type;

typedef struct _mol_type {
  struct _mol_type *next;
  res_type *residua;
  int nres;
  unsigned char *r14;
  char *name;
  uchar *seq;
  char **contacts;
  real **cutoffs;
  struct _mol_type *prev;
} mol_type;

#define MIN(a,b) (a<b?a:b)
#define MAX(a,b) (a>b?a:b)

mol_type *chain = NULL;


real rnd(void)
{
  return 0.001*(real)(rand()%1000);
}

atom_type *new_atom(void)
{
  atom_type *tmpatom;

    tmpatom = (atom_type*) calloc(sizeof(atom_type),1);
    if (tmpatom) {
      tmpatom->x=tmpatom->y=tmpatom->z=0.;
      tmpatom->name=NULL;
      tmpatom->num=tmpatom->locnum=tmpatom->flag=0;
      tmpatom->next=tmpatom->prev=NULL;
    }

  return tmpatom;
}

res_type* new_res(void)
{
  res_type *tmpres;

    tmpres = (res_type*) calloc(sizeof(res_type),1);
    if (tmpres) {
      tmpres->num=0;
      tmpres->name=NULL;
      tmpres->atoms=NULL;
      tmpres->chain=' ';
      tmpres->next=tmpres->prev=NULL;
    }

  return tmpres;
}

mol_type *new_mol(void)
{
  mol_type *tmpmol;

    tmpmol = (mol_type*) calloc(sizeof(mol_type),1);
    if (tmpmol) {
      tmpmol->name=NULL;
      tmpmol->residua=NULL;
      tmpmol->next=tmpmol->prev=NULL;
    }

  return tmpmol;
}

void add_atom(atom_type* atomlist, atom_type* newatom)
{
  atom_type *tmpatom;

    if (!atomlist)
      atomlist=newatom;
    else {
      tmpatom=atomlist->next;
      atomlist->next=newatom;
      newatom->prev=atomlist;
      newatom->next=tmpatom;
      if (tmpatom) tmpatom->prev=newatom;
    }
}

void add_res(res_type* reslist, res_type* newres)
{
  res_type *tmpres;

    if (!reslist)
      reslist=newres;
    else {
      tmpres=reslist->next;
      reslist->next=newres;
      newres->prev=reslist;
      newres->next=tmpres;
      if (tmpres) tmpres->prev=newres;
    }
}

void add_mol(mol_type* mollist, mol_type* newmol)
{
  mol_type *tmpmol;

    if (!mollist)
      mollist=newmol;
    else {
      tmpmol=mollist->next;
      mollist->next=newmol;
      newmol->prev=mollist;
      newmol->next=tmpmol;
      if (tmpmol) tmpmol->prev=newmol;
    }
}

void delete_atom(atom_type* atom)
{
  atom_type *tmpatom;

    if (atom->prev) atom->prev->next=atom->next;
    if (atom->next) atom->next->prev=atom->prev;
    if (atom->name) free(atom->name);
    free(atom);
    atom=NULL;
}

void delete_res(res_type* res)
{
  res_type *tmpres;
  atom_type *tmpatom;

    if (res->prev) res->prev->next=res->next;
    if (res->next) res->next->prev=res->prev;
    if (res->name) free(res->name);
    if (res->atoms) {
      while (res->atoms) {
        tmpatom = res->atoms->next;
        delete_atom(res->atoms);
        res->atoms=tmpatom;
      }
    }
    free(res);
    res=NULL;
}

void delete_mol(mol_type* mol)
{
  mol_type *tmpmol;
  res_type *tmpres;
  int i;

    if (mol->prev) mol->prev->next=mol->next;
    if (mol->next) mol->next->prev=mol->prev;
    if (mol->name) free(mol->name);
    if (mol->residua) {
      while (mol->residua) {
        tmpres = mol->residua->next;
        delete_res(mol->residua);
        mol->residua=tmpres;
      }
    }
    if (mol->contacts) {
      for (i=0; i<mol->nres; i++) free(mol->contacts[i]);
      free(mol->contacts);
    }
    if (mol->cutoffs) {
      for (i=0; i<mol->nres; i++) free(mol->cutoffs[i]);
      free(mol->cutoffs);
    }
    free(mol);
    mol=NULL;
}


atom_type* get_last_atom(atom_type* atom)
{
    while (atom->next) atom=atom->next;

  return atom;
}

res_type* get_last_res(res_type* res)
{
    while (res->next) res=res->next;

  return res;
}

mol_type *get_last_mol(mol_type* mol)
{
    while (mol->next) mol=mol->next;

  return mol;
}

char setseq(char* aaname)
{
  int i;

    for (i=0; i<21; i++) {
      if ((aaname[0]==AA_NAMES[i][0]) &&
          (aaname[1]==AA_NAMES[i][1]) &&
          (aaname[2]==AA_NAMES[i][2]))
         break;
    }
    
    if (i==21) {
      if (!strcmp(aaname, "GLX"))
        return 'E';
      if (!strcmp(aaname, "ASX"))
        return 'D';
      if (!strcmp(aaname, "HID"))
        return 'H';
      if (!strcmp(aaname, "MSE"))
        return 'M';
      if (!strcmp(aaname, "SEP"))
        return 'S';
      if (!strcmp(aaname, "TPO"))
        return 'T';
      if (!strcmp(aaname, "PTR"))
        return 'Y';
      i--;
    }

  return SHORT_AA_NAMES[i];
}

int orient(res_type *res1, res_type *res2)
{
  real x1, y1, z1;
  real x2, y2, z2;
  real cax, cay, caz;
  real len, vect, angle;
  atom_type *atom;

    if (!res1 || !res2) return 0;

    atom=res1->atoms;
    cax=cay=caz=0.;
    while (atom) {
      if (!strncmp(atom->name,"CA",2)) {
        cax=atom->x; cay=atom->y; caz=atom->z;
      }
      atom=atom->next;
    }
    x1=res1->sgx-cax; y1=res1->sgy-cay; z1=res1->sgz-caz;
    if (x1==0. && y1==0. && z1==0.) x1+=1.0;

    atom=res2->atoms;
    cax=cay=caz=0.;
    while (atom) {
      if (!strncmp(atom->name,"CA",2)) {
        cax=atom->x; cay=atom->y; caz=atom->z;
      }
      atom=atom->next;
    }
    x2=res2->sgx-cax; y2=res2->sgy-cay; z2=res2->sgz-caz;
    if (x2==0. && y2==0. && z2==0.) x2+=1.0;

    vect = x1*x2+y1*y2+z1*z2;
    len = sqrt(x1*x1+y1*y1+z1*z1)*sqrt(x2*x2+y2*y2+z2*z2);
    if (len) vect /= len;

    angle=RADDEG*acos(vect);

    if (angle>120.) return 1; /*anti*/
    if (angle>60.) return 2;  /*mid*/

  return 3; /*par*/
}

int res_contact(res_type *res1, res_type *res2) {
  atom_type *atoms1, *atoms2;
  real dx, dy, dz;

    atoms1 = res1->atoms;
    while (atoms1) {
      atoms2 = res2->atoms;
      while (atoms2) {
        dx=atoms1->x-atoms2->x;
        dy=atoms1->y-atoms2->y;
        dz=atoms1->z-atoms2->z;
        if ((atoms1->flag & FLAG_SIDECHAIN) && (atoms2->flag & FLAG_SIDECHAIN) && (dx*dx+dy*dy+dz*dz<20.25)) {
          return 1;
        }
        atoms2=atoms2->next;
      }
      atoms1=atoms1->next;
    }

  return 0;
}

int read_pdb_file(char* filename, mol_type* molecules, char *realname)
{
  FILE *inp;
  char buffer[1000];
  char atmname[10];
  char resname[10];
  char version;
  int prevresnum, resnum, atmnum, locatmnum, num, locnum=0, i, j;
  atom_type *prevatom1, *prevatom2, *prevatom3, *prevatom4;
  int sgnum, cc, nres, ok, natom;
  real sgx, sgy, sgz;
  res_type *res, *test1, *test2;
  atom_type *atom;
  real x, y, z;
  real dist;
  unsigned char bin;
  int warn=0;
  real cutoff;

    if (_VERBOSE) printf("Reading input file %s...\n", filename);

    inp = fopen(filename, "r");
    if (!inp) {
      if (_VERBOSE) printf("ERROR: can't open %s !!!\n", filename);
      return FILE_NOT_FOUND;
    }

    molecules->nres=0;
    molecules->name=(char*)calloc(strlen(realname)+1,1);
    strcpy(molecules->name, realname);

    atmname[3]=0;
    resname[3]=0;
    prevresnum=-666;
    locatmnum=0;
    sgnum=0;
    sgx=sgy=sgz=0.;
    res=NULL;
    while (!feof(inp)) {
      if (fgets(buffer, 1000, inp)!=buffer) break;
      if (!strncmp(buffer, "END", 3) || !strncmp(buffer, "TER", 3)) break; // end of file; only single molecule read
      if (!strncmp(buffer, "ATOM", 4) || !strncmp(buffer, "HETATM", 6)) {
        if (buffer[16]!=' ' && buffer[16]!='A') continue;
        sscanf(&buffer[22], "%d", &resnum);
        strncpy(resname, &buffer[17], 3);
        strncpy(atmname, &buffer[13], 3);
        if (resnum==prevresnum && !strncmp(atmname, "N ", 2)) {
          if (_VERBOSE) printf("WARNING: fault in numeration at residuum %s[%d]\n", resname, resnum);
          warn=1;
        }
        if (atmname[0]=='H') continue;
        if (resnum!=prevresnum || !strncmp(atmname, "N ", 2)) {
          prevresnum=resnum;
          if (res) {
            if (sgnum) {
              res->sgx=sgx/(real)sgnum;
              res->sgy=sgy/(real)sgnum;
              res->sgz=sgz/(real)sgnum;
            } else {
              res->sgx=res->sgy=res->sgz=0.;
            }
          }
          locatmnum=0;
          version=' ';
          res = new_res();
          sgnum=0;
          sgx=sgy=sgz=0.;
          molecules->nres++;
          res->name = calloc(strlen(resname)+1, 1);
          res->type = AA_NUMS[setseq(resname)];
          res->locnum=locnum++;
          res->num = resnum;
          res->natoms=0;
          res->chain = buffer[21];
          strcpy(res->name, resname);
          if (molecules->residua) {
            add_res(get_last_res(molecules->residua), res);
          } else {
            molecules->residua = res;
          }
        }
        atom = new_atom();
        atom->res = res;
        res->natoms++;
        locatmnum++;
        sscanf(&buffer[7], "%d", &atmnum);
        sscanf(&buffer[30], "%lf", &x);
        sscanf(&buffer[38], "%lf", &y);
        sscanf(&buffer[46], "%lf", &z);
        version = buffer[16];
        atom->name = calloc(strlen(atmname)+1,1);
        strcpy(atom->name, atmname);
        atom->x=x; atom->y=y; atom->z=z;
        atom->num = atmnum;
        atom->locnum = locatmnum;
        if ((atmname[0]=='S' && atmname[1]=='C')||(atmname[0]=='C' && atmname[1]=='M')) {
          res->cmx = x;
          res->cmy = y;
          res->cmz = z;
          res->pdbsg=1;
          if (res->type<20) {
            res->protein=1;
          }
        } else
        if (!( ((atmname[0]=='C' || atmname[0]=='N' || atmname[0]=='O') && atmname[1]==' ') ||
               (atmname[0]=='H') ||
               (atmname[0]=='C' && atmname[1]=='A') ||
               (atmname[0]=='O' && atmname[1]=='X' && atmname[2]=='T') ) ) {
          sgx+=x;
          sgy+=y;
          sgz+=z;
          sgnum++;
          atom->flag |= FLAG_SIDECHAIN;
        } else
          atom->flag |= FLAG_BACKBONE;
        if (atmname[0]=='C' && atmname[1]=='A') {
          atom->flag |= FLAG_BACKBONE;
          if (res->type<20) {
            res->protein=1;
          }
          if (!res->pdbsg) {
            res->cmx = x;
            res->cmy = y;
            res->cmz = z;
          }
        }
        if (atmname[0]=='C' && atmname[1]=='M') {
          atom->flag |= FLAG_SCM;
        }
        if (atmname[0]=='S' && atmname[1]=='C') {
          atom->flag |= FLAG_SCM;
        }
        if (res->atoms) {
          add_atom(get_last_atom(res->atoms), atom);
        } else {
          res->atoms = atom;
        }
      }
    }

    if (res) {
      if (sgnum) {
        res->sgx=sgx/(real)sgnum;
        res->sgy=sgy/(real)sgnum;
        res->sgz=sgz/(real)sgnum;
      } else {
        res->sgx=res->sgy=res->sgz=0.;
      }
    }
    fclose(inp);

    molecules->seq = (uchar*)calloc(sizeof(uchar)*molecules->nres+1,1);
    res=molecules->residua; i=0;
    while (res) {
      molecules->seq[i++]=(uchar)AA_NUMS[(int)setseq(res->name)];
      res=res->next;
    }

  if (!warn) return FILE_SUCCESS; else return FILE_WARNING;
}

#define bool int
#define true 1
#define false 0

real calc_ca_energy(atom_type **c_alpha, real **new_c_alpha, real **init_c_alpha, real **gradient, real alpha, real *ene, bool calc_gradient)
{
  int i, j;
  real dx, dy, dz;
  real dist, ddist, ddist2;
  real new_e_pot;
  real theta0, tdif, th, aa, bb, ab;
  real ff0, ff2, dth, m0, m2, grad, f0[3], f2[3];
  real adiff[3], bdiff[3];
  real deriv, theta, dtheta, len1, len2, cos_theta, sin_theta;
  real dx1, dy1, dz1;
  real dx2, dy2, dz2;
  real dx3, dy3, dz3;
  real vx1, vy1, vz1;
  real vx2, vy2, vz2;
  real vx3, vy3, vz3;

  real r12x, r12y, r12z;
  real r32x, r32y, r32z;
  real d12, d32, d12inv, d32inv, c1, c2, diff;
  real f1x, f1y, f1z;
  real f2x, f2y, f2z;
  real f3x, f3y, f3z;

        for (i=0; i<chain_length; i++) {
          new_c_alpha[i][0]=c_alpha[i]->x+alpha*gradient[i][0];
          new_c_alpha[i][1]=c_alpha[i]->y+alpha*gradient[i][1];
          new_c_alpha[i][2]=c_alpha[i]->z+alpha*gradient[i][2];
        }

        new_e_pot = 0.0;

        ene[0]=ene[1]=ene[2]=ene[3]=0.0;

        for (i=0; i<chain_length; i++) {
#ifdef CALC_C_ALPHA_START
          dx=new_c_alpha[i][0]-init_c_alpha[i][0];
          dy=new_c_alpha[i][1]-init_c_alpha[i][1];
          dz=new_c_alpha[i][2]-init_c_alpha[i][2];
          dist=sqrt(dx*dx+dy*dy+dz*dz);
          ddist = -dist;
          if (dist>_CA_START_DIST) {
            ddist2=dist*dist;
            new_e_pot+=CA_START_K*ddist2;
            ene[1] += CA_START_K*ddist2;
            if (calc_gradient) {
              grad = ddist * (-2.0*CA_START_K)/dist;
              gradient[i][0]-=grad*dx;
              gradient[i][1]-=grad*dy;
              gradient[i][2]-=grad*dz;
            }
          }

#endif


#ifdef CALC_C_ALPHA
          if (i>0) {
            dx=new_c_alpha[i][0]-new_c_alpha[i-1][0];
            dy=new_c_alpha[i][1]-new_c_alpha[i-1][1];
            dz=new_c_alpha[i][2]-new_c_alpha[i-1][2];
            dist=sqrt(dx*dx+dy*dy+dz*dz);
            if (c_alpha[i]->cispro) {
              ddist=CA_DIST_CISPRO-dist;
//              if (fabs(ddist)<CA_DIST_CISPRO_TOL) ddist=0.0;
            } else {
              ddist=CA_DIST-dist;
//              if (fabs(ddist)<CA_DIST_TOL) ddist=0.0;
            }
            ddist2=ddist*ddist;
            new_e_pot+=CA_K*ddist2;
            ene[0] += CA_K*ddist2;
            if (calc_gradient) {
              grad = ddist * (-2.0*CA_K)/dist;
              gradient[i][0]-=grad*dx;
              gradient[i][1]-=grad*dy;
              gradient[i][2]-=grad*dz;
              gradient[i-1][0]+=grad*dx;
              gradient[i-1][1]+=grad*dy;
              gradient[i-1][2]+=grad*dz;
            }
          }
#endif

#ifdef CALC_C_ALPHA_XVOL
          for (j=0;j<i;j++) {
            if (abs(i-j)>2) {
              dx=new_c_alpha[i][0]-new_c_alpha[j][0];
              dy=new_c_alpha[i][1]-new_c_alpha[j][1];
              dz=new_c_alpha[i][2]-new_c_alpha[j][2];
              dist=sqrt(dx*dx+dy*dy+dz*dz);
              ddist = dist-_CA_XVOL_DIST;
              if (dist<_CA_XVOL_DIST) {
                ddist2 = dist*dist;
                new_e_pot+=CA_XVOL_K*ddist2;
                ene[3] += CA_XVOL_K*ddist2;
                if (calc_gradient) {
                  grad = ddist*(8.0*CA_XVOL_K)/dist;
                  gradient[i][0]-=grad*dx;
                  gradient[i][1]-=grad*dy;
                  gradient[i][2]-=grad*dz;
                  gradient[j][0]+=grad*dx;
                  gradient[j][1]+=grad*dy;
                  gradient[j][2]+=grad*dz;
                }
              }
            }
          }
#endif

#ifdef CALC_C_ALPHA_ANGLES

        if (i>0 && i<chain_length-1) {
          r12x=new_c_alpha[i-1][0]-new_c_alpha[i][0];
          r12y=new_c_alpha[i-1][1]-new_c_alpha[i][1];
          r12z=new_c_alpha[i-1][2]-new_c_alpha[i][2];
          r32x=new_c_alpha[i+1][0]-new_c_alpha[i][0];
          r32y=new_c_alpha[i+1][1]-new_c_alpha[i][1];
          r32z=new_c_alpha[i+1][2]-new_c_alpha[i][2];
          d12 = sqrt(r12x*r12x+r12y*r12y+r12z*r12z);
          d32 = sqrt(r32x*r32x+r32y*r32y+r32z*r32z);
          cos_theta = (r12x*r32x+r12y*r32y+r12z*r32z)/(d12*d32);
          if (cos_theta>1.0)
            cos_theta = 1.0;
          else
          if (cos_theta<-1.0)
            cos_theta = -1.0;
          sin_theta = sqrt(1.0-cos_theta*cos_theta);
          theta = acos(cos_theta);

          if (RADDEG*theta<80.)
            diff = theta-80.*DEGRAD;
          else
          if (RADDEG*theta>150.)
            diff = theta-150.*DEGRAD;
          else
            diff = 0.0;

          new_e_pot += CA_ANGLE_K*diff*diff;
          ene[2] += CA_ANGLE_K*diff*diff;
          if (calc_gradient) {
            d12inv = 1.0/d12;
            d32inv = 1.0/d32;
            diff *= (-2.0*CA_ANGLE_K)/sin_theta;
            c1 = diff*d12inv;
            c2 = diff*d32inv;
            f1x = c1*(r12x*(d12inv*cos_theta)-r32x*d32inv);
            f1y = c1*(r12y*(d12inv*cos_theta)-r32y*d32inv);
            f1z = c1*(r12z*(d12inv*cos_theta)-r32z*d32inv);
            f3x = c2*(r32x*(d32inv*cos_theta)-r12x*d12inv);
            f3y = c2*(r32y*(d32inv*cos_theta)-r12y*d12inv);
            f3z = c2*(r32z*(d32inv*cos_theta)-r12z*d12inv);
            f2x = -f1x-f3x;
            f2y = -f1y-f3y;
            f2z = -f1z-f3z;
            gradient[i-1][0]+=f1x;
            gradient[i-1][1]+=f1y;
            gradient[i-1][2]+=f1z;
            gradient[i][0]+=f2x;
            gradient[i][1]+=f2y;
            gradient[i][2]+=f2z;
            gradient[i+1][0]+=f3x;
            gradient[i+1][1]+=f3y;
            gradient[i+1][2]+=f3z;
          }
        }

#endif

        }

//printf("ene[3] = %f\n", ene[3]);

  return new_e_pot;
}

/*
 *  Steepest gradient optimization using v=k*(r0-r)^2
 *  k = CA_K, r0 = CA_DIST
 */
void ca_optimize(char *tname, char *iname)
{
  char buf[1000];
  int i, j, hx, my_iter;
  real dx, dy, dz, dd, dist, dist2, dist3, ddist, ddist2;
  real e_pot, new_e_pot, grad, alpha, e_pot1, e_pot2, e_pot3;
  real adiff[3], bdiff[3];
  real ff0, ff2, aa, ab, bb, th, tdif, dth, m0, m2;
  real theta0, deg_th, maxgrad, sum;
  real f0[3], f2[3];
  real x, y, z;
  int numsteps, numsteps2, msteps;
  int *sec;
  real **new_c_alpha, **gradient, **init_c_alpha, last_alpha, tmp, last_good_alpha, d_alpha, last_e_pot;
  atom_type *atom, **c_alpha;
  res_type *res;
  FILE *inp, *out;
  int mnum, init, ok;
  real alpha1, alpha2, alpha3, a0;
  real ene1, ene2, ene3, e0;
  real energies[4];
  real w1, w2, w3, eps;
  real gnorm, last_gnorm;
  int mode, fcnt;


    if (_CA_TRAJECTORY) {
      out = fopen(tname,"w");
      if (out) fclose(out);
    }

    if (_VERBOSE) printf("Alpha carbons optimization...\n");
      
    new_c_alpha = (real**)calloc(sizeof(real*)*(chain_length+1),1);
    init_c_alpha = (real**)calloc(sizeof(real*)*(chain_length+1),1);
    for (i=0;i<=chain_length;i++) {
      new_c_alpha[i] = (real*)calloc(sizeof(real)*3,1);
      init_c_alpha[i] = (real*)calloc(sizeof(real)*3,1);
    }
    gradient = (real**)calloc(sizeof(real*)*(chain_length+1),1);
    for (i=0;i<=chain_length;i++) {
      gradient[i] = (real*)calloc(sizeof(real)*3,1);
    }

    c_alpha = (atom_type**)calloc(sizeof(atom_type*)*(chain_length+1),1);

    i = 0;
    res = chain->residua;
    while (res) {
      atom = res->atoms;
      while (atom) {
        if (atom->name[0]=='C' && atom->name[1]=='A') {
          if (i<chain_length) {
            c_alpha[i] = atom;
            i++;
            break;
          } else {
            if (_VERBOSE) printf("WARNING: number of C-alpha atoms exceeds the chain length!\n");
            break;
          }
        }
        atom = atom->next;
      }
      res = res->next;
    }

    if (i<chain_length) chain_length = i;
      
    for (i=0; i<chain_length; i++) {
      init_c_alpha[i][0] = c_alpha[i]->x;
      init_c_alpha[i][1] = c_alpha[i]->y;
      init_c_alpha[i][2] = c_alpha[i]->z;
    }

    if (_CISPRO) {
      for (i=1; i<chain_length; i++) {
        dx = c_alpha[i]->x-c_alpha[i-1]->x;
        dy = c_alpha[i]->y-c_alpha[i-1]->y;
        dz = c_alpha[i]->z-c_alpha[i-1]->z;
        dd = sqrt(dx*dx+dy*dy+dz*dz);
        if ((setseq(c_alpha[i]->res->name)=='P') && (dd>CA_DIST_CISPRO-5*CA_DIST_CISPRO_TOL) && (dd<CA_DIST_CISPRO+5*CA_DIST_CISPRO_TOL)) {
          if (_VERBOSE) printf("Probable cis-proline found at postion %d\n", c_alpha[i]->res->num);
          c_alpha[i]->cispro = 1;
        }
      }
    }

    if (_CA_RANDOM) {
      if (_VERBOSE) printf("Generating random C-alpha coordinates...\n");
      c_alpha[0]->x = 0.0;
      c_alpha[0]->y = 0.0;
      c_alpha[0]->z = 0.0;
      for (i=1;i<chain_length;i++) {
        dx = 0.01*(100-rand()%200);
        dy = 0.01*(100-rand()%200);
        dz = 0.01*(100-rand()%200);
        dd = 3.8/sqrt(dx*dx+dy*dy+dz*dz);
        dx *= dd;
        dy *= dd;
        dz *= dd;
        c_alpha[i]->x = c_alpha[i-1]->x+dx;
        c_alpha[i]->y = c_alpha[i-1]->y+dy;
        c_alpha[i]->z = c_alpha[i-1]->z+dz;
      }
    }

    if (iname) {
      inp = fopen(iname,"r");
      if (inp) {
        if (_VERBOSE) printf("Reading initial structure %s...\n", iname);
        i = 0;
        while (!feof(inp)) {
          if (fgets(buf,1000,inp)==buf && buf[13]=='C' && buf[14]=='A') {
            if (i<chain_length) {
              if (sscanf(&buf[30],"%lf%lf%lf",&x,&y,&z)==3) {
                c_alpha[i]->x = x;
                c_alpha[i]->y = y;
                c_alpha[i]->z = z;
                i++;
              }
            } else {
              if (_VERBOSE) printf("WARNING: number of ini-file C-alpha atoms exceeds the chain length!\n");
              break;
            }
          }
        }
        fclose(inp);
      } else
        if (_VERBOSE) printf("WARNING: can't read initial corrdinates %s\n", iname);
    }

    mnum = 1;
    mode = 0;
    init = 0;
    numsteps=numsteps2=0;
    last_alpha = 0.0;


    if (_VERBOSE) printf("Optimizing alpha carbons...\n");

    eps = 0.5;

    fcnt=0;

    last_gnorm = 1000.;

    do {
      last_e_pot = e_pot;

      if (_CA_TRAJECTORY) {
        out = fopen(tname,"a");
        if (out) {
          fprintf(out,"MODEL  %d\n",mnum++);
          for (i=0; i<chain_length; i++) {
            fprintf(out, "ATOM  %5d  %-3s %3s %c%4d    %8.3f%8.3f%8.3f\n",
                    i+1, "CA ", c_alpha[i]->res->name, ' ', c_alpha[i]->res->num,
  				          c_alpha[i]->x, c_alpha[i]->y, c_alpha[i]->z);

          }
          fprintf(out,"ENDMDL\n");
          fclose(out);
        }
      }

// calculate gradients

      e_pot=e_pot1=e_pot2=e_pot3=0.;

      for (i=0; i<chain_length; i++)
        gradient[i][0]=gradient[i][1]=gradient[i][2]=0.;

      e_pot = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, 0.0, energies, true);

      if (_VERBOSE && !init) {
        printf("Initial energy: bond=%.5lf angle=%.5f restraints=%.5f xvol=%.5f total=%.5f\n", energies[0], energies[2], energies[1], energies[3], e_pot);
      }

      if (!init) init=1;

// LINE SEARCH

      alpha1 = -1.0;
      alpha2 = 0.0;
      alpha3 = 1.0;

      ene1 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, alpha1, energies, false);
      ene2 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, alpha2, energies, false);
      ene3 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, alpha3, energies, false);

      msteps = 0;
      while (ene2>MIN(ene1,ene3) && msteps<_CA_ITER) {
        msteps++;
        alpha1 *= 2.0;
        alpha3 *= 2.0;
        ene1 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, alpha1, energies, false);
        ene3 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, alpha3, energies, false);
      }

      msteps = 0;
      do {
        if (alpha3-alpha2>alpha2-alpha1) {
          a0 = 0.5*(alpha2+alpha3);
          e0 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, a0, energies, false);
          e0 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, a0-1e-5, energies, false);
          e0 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, a0+1e-5, energies, false);
          e0 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, a0, energies, false);
          if (e0<ene2) {
            alpha1 = alpha2;
            alpha2 = a0;
            ene1 = ene2;
            ene2 = e0;
          } else {
            alpha3 = a0;
            ene3 = e0;
          }
        } else {
          a0 = 0.5*(alpha1+alpha2);
          e0 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, a0, energies, false);
          e0 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, a0-1e-5, energies, false);
          e0 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, a0+1e-5, energies, false);
          e0 = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, a0, energies, false);
          if (e0<ene2) {
            alpha3 = alpha2;
            alpha2 = a0;
            ene3 = ene2;
            ene2 = e0;
          } else {
            alpha1 = a0;
            ene1 = e0;
          }
        }
        msteps++;
      } while (alpha3-alpha1>1e-6 && msteps<20);

      last_alpha = alpha2;
      e_pot = ene2;

      for (i=0; i<chain_length; i++) {
        c_alpha[i]->x=c_alpha[i]->x+(last_alpha+last_alpha*(rnd()-0.5)*eps)*gradient[i][0];
        c_alpha[i]->y=c_alpha[i]->y+(last_alpha+last_alpha*(rnd()-0.5)*eps)*gradient[i][1];
        c_alpha[i]->z=c_alpha[i]->z+(last_alpha+last_alpha*(rnd()-0.5)*eps)*gradient[i][2];
      }

      e_pot = calc_ca_energy(c_alpha, new_c_alpha, init_c_alpha, gradient, 0.0, energies, false);

      eps *= 0.75;
      if (eps<1e-3) eps=0.0;

      numsteps++;

      gnorm = 0.0;

	  for (i=0; i<chain_length; i++) {
        gnorm += gradient[i][0]*gradient[i][0] + gradient[i][1]*gradient[i][1] + gradient[i][2]*gradient[i][2];
      }

      gnorm = sqrt(gnorm/(double)chain_length);

      if (last_gnorm-gnorm<1e-3) fcnt++;

      last_gnorm = gnorm;

    } while ( (fcnt<3) &&  (gnorm>0.01) && (numsteps<_CA_ITER));


     if (_VERBOSE) {
        for (i=0; i<chain_length; i++) {

#ifdef CALC_C_ALPHA
          if (i>0) {
            dx=c_alpha[i]->x-c_alpha[i-1]->x;
            dy=c_alpha[i]->y-c_alpha[i-1]->y;
            dz=c_alpha[i]->z-c_alpha[i-1]->z;
            dist=sqrt(dx*dx+dy*dy+dz*dz);
            if (c_alpha[i]->cispro) {
              ddist=CA_DIST_CISPRO-dist;
              if (fabs(ddist)<CA_DIST_CISPRO_TOL) ddist=0.0;
            } else {
              ddist=CA_DIST-dist;
              if (fabs(ddist)<CA_DIST_TOL) ddist=0.0;
            }
            ddist2=ddist*ddist;
       	    if (fabs(ddist)>=CA_DIST_TOL) printf("WARNING: distance %d = %.3lf A\n", i, dist);
          }
#endif
        }

        for (i=0; i<chain_length; i++) {
#ifdef CALC_C_ALPHA_ANGLES
          if (i>0 && i<chain_length-1) {
            aa=ab=bb=0.0;
            adiff[0]=c_alpha[i-1]->x-c_alpha[i]->x;
            bdiff[0]=c_alpha[i+1]->x-c_alpha[i]->x;
            aa+=adiff[0]*adiff[0];
            ab+=adiff[0]*bdiff[0];
            bb+=bdiff[0]*bdiff[0];
            adiff[1]=c_alpha[i-1]->y-c_alpha[i]->y;
            bdiff[1]=c_alpha[i+1]->y-c_alpha[i]->y;
            aa+=adiff[1]*adiff[1];
            ab+=adiff[1]*bdiff[1];
            bb+=bdiff[1]*bdiff[1];
            adiff[2]=c_alpha[i-1]->z-c_alpha[i]->z;
            bdiff[2]=c_alpha[i+1]->z-c_alpha[i]->z;
            aa+=adiff[2]*adiff[2];
            ab+=adiff[2]*bdiff[2];
            bb+=bdiff[2]*bdiff[2];

            th=ab/sqrt(aa*bb);
            if (th<-1.0) th=-1.0;
            if (th>1.0) th=1.0;
            th=acos(th);
            deg_th=RADDEG*th;
            if (deg_th>150.) theta0=DEGRAD*150.; else
            if (deg_th<75.) theta0=DEGRAD*75.; else
            theta0=th;
       	    if (fabs(deg_th-RADDEG*theta0)>1.0) printf("WARNING: angle %d = %.3lf degrees\n", i, deg_th);
          }
#endif
        }
      }

    if (_VERBOSE) printf("Optimization done after %d step(s).\nFinal energy: bond=%.5lf angle=%.5f restraints=%.5f xvol=%.5f total=%.5f\n", numsteps, energies[0], energies[2], energies[1], energies[3], e_pot);

    if (_CA_TRAJECTORY) {
      out = fopen(tname,"a");
      if (out) {
        fprintf(out,"END\n");
      }
    }

    for (i=0;i<chain_length+1;i++) {
      free(init_c_alpha[i]);
      free(new_c_alpha[i]);
      free(gradient[i]);
    }
    free(new_c_alpha);
    free(gradient);
    free(c_alpha);
    free(init_c_alpha);
}

void center_chain(mol_type *mol)
{
  real cx, cy, cz;
  int natom;
  res_type *res;
  atom_type *atom;

    cx = cy = cz = 0.0;
    natom = 0;

    res = mol->residua;
    while (res) {
      atom = res->atoms;
      while (atom) {
        cx += atom->x;
        cy += atom->y;
        cz += atom->z;
        natom++;
  			atom=atom->next;
  		}
      res = res->next;
    }

    cx /= (real)natom;
    cy /= (real)natom;
    cz /= (real)natom;

    if (_VERBOSE) printf("Molecule center: %8.3f %8.3f %8.3f -> 0.000 0.000 0.000\n", cx, cy, cz);

    res = mol->residua;
    while (res) {
      atom = res->atoms;
      while (atom) {
        atom->x -= cx;
        atom->y -= cy;
        atom->z -= cz;
        natom++;
  		atom=atom->next;
  	  }
      res = res->next;
    }

}

void write_pdb(char *name, mol_type *mol)
{
  FILE *out;
  res_type *res;
  atom_type *atom;
  int anum;

    out = fopen(name,"w");
    if (!out) {
      if (_VERBOSE) printf("Can't open output file!\n");
      return;
    }
    fprintf(out,"REMARK 999 REBUILT BY PULCHRA V.%.2f\n", PULCHRA_VERSION);
    anum=1;
    res = mol->residua;
    while (res) {
      if (res->protein) {
        if (!_BB_REARRANGE) {
          atom = res->atoms;
          while (atom) {
            if (!(atom->name[0]=='D' && atom->name[1]=='U') &&
                !(atom->name[0]=='S' && atom->name[1]=='C') &&
                !(atom->name[0]=='C' && atom->name[1]=='M') &&
                !(atom->name[0]=='H' && !_REBUILD_H))
              fprintf(out, "ATOM  %5d  %-3s %3s %c%4d    %8.3f%8.3f%8.3f\n",
                            anum++, atom->name, res->name, ' ', res->num,
    	    				          atom->x, atom->y, atom->z);
      			atom=atom->next;
      		}
      	} else {
          atom = res->atoms;
          while (atom) {
            if (!(atom->name[0]=='D' && atom->name[1]=='U') &&
                !(atom->name[0]=='S' && atom->name[1]=='C') &&
                !(atom->name[0]=='C' && atom->name[1]==' ') &&
                !(atom->name[0]=='O' && atom->name[1]==' ') &&
                !(atom->name[0]=='C' && atom->name[1]=='M') &&
                !(atom->name[0]=='H' && !_REBUILD_H))
              fprintf(out, "ATOM  %5d  %-3s %3s %c%4d    %8.3f%8.3f%8.3f\n",
                            anum++, atom->name, res->name, ' ', res->num,
    	    				          atom->x, atom->y, atom->z);
      			atom=atom->next;
      		}
          atom = res->atoms;
          while (atom) {
            if (((atom->name[0]=='C' && atom->name[1]==' ') ||
                (atom->name[0]=='O' && atom->name[1]==' ')) &&
               !(atom->name[0]=='H' && !_REBUILD_H))
              fprintf(out, "ATOM  %5d  %-3s %3s %c%4d    %8.3f%8.3f%8.3f\n",
                            anum++, atom->name, res->name, ' ', res->num,
    	    				          atom->x, atom->y, atom->z);
      			atom=atom->next;
      		}
      	}
      }
      res = res->next;
    }
    fprintf(out,"TER\nEND\n");
    fclose(out);
}


void write_pdb_sg(char *name, mol_type *mol)
{
  FILE *out;
  res_type *res;
  atom_type *atom;
  int anum;

    out = fopen(name,"w");
    if (!out) {
      if (_VERBOSE) printf("Can't open output file!\n");
      return;
    }
    fprintf(out,"REMARK 999 REBUILT BY PULCHRA V.%.2f\n", PULCHRA_VERSION);
    anum=1;
    res = mol->residua;
    while (res) {
      if (res->protein) {
        atom = res->atoms;
        while (atom) {
          if ((atom->name[0]=='C' && atom->name[1]=='A'))
            fprintf(out, "ATOM  %5d  %-3s %3s %c%4d    %8.3f%8.3f%8.3f\n",
                          anum++, atom->name, res->name, ' ', res->num,
    	  				          atom->x, atom->y, atom->z);
    			atom=atom->next;
    		}
        fprintf(out, "ATOM  %5d  %-3s %3s %c%4d    %8.3f%8.3f%8.3f\n",
                      anum++, "CM ", res->name, ' ', res->num,
    				          res->cmx, res->cmy, res->cmz);
    	}
      res = res->next;
    }
    fprintf(out,"TER\nEND\n");
    fclose(out);
}

real calc_distance(real x1, real y1, real z1,
							 		  real x2, real y2, real z2)
{
  real dx,dy,dz;
  real dist2;

    dx = (x1) - (x2);
    dy = (y1) - (y2);
    dz = (z1) - (z2);
    if (dx || dy || dz ) {
      dist2 = dx*dx+dy*dy+dz*dz;
      return (sqrt(dist2));
    } else
      return 0.0;
}

real calc_r14(real x1, real y1, real z1,
							 real x2, real y2, real z2,
							 real x3, real y3, real z3,
							 real x4, real y4, real z4)
{
  real r, dx, dy, dz;
  real vx1, vy1, vz1, vx2, vy2, vz2, vx3, vy3, vz3;
  real hand;

    dx = x4-x1;
    dy = y4-y1;
    dz = z4-z1;

    r = sqrt(dx*dx+dy*dy+dz*dz);

    vx1=x2-x1;
    vy1=y2-y1;
    vz1=z2-z1;
    vx2=x3-x2;
    vy2=y3-y2;
    vz2=z3-z2;
    vx3=x4-x3;
    vy3=y4-y3;
    vz3=z4-z3;

    hand = (vy1*vz2-vy2*vz1)*vx3+
           (vz1*vx2-vz2*vx1)*vy3+
           (vx1*vy2-vx2*vy1)*vz3;

    if (hand<0) r=-r;

  return r;
}

real superimpose2(real **coords1, real **coords2, int npoints, real **tpoints, int ntpoints)
{
  real mat_s[3][3], mat_a[3][3], mat_b[3][3], mat_g[3][3];
  real mat_u[3][3], tmp_mat[3][3];
  real val, d, alpha, beta, gamma, x, y, z;
  real cx1, cy1, cz1, cx2, cy2, cz2, tmpx, tmpy, tmpz;
  int i, j, k, n;

    cx1=cy1=cz1=cx2=cy2=cz2=0.;

    for (i=0; i<npoints; i++) {
      cx1+=coords1[i][0];
      cy1+=coords1[i][1];
      cz1+=coords1[i][2];
      cx2+=coords2[i][0];
      cy2+=coords2[i][1];
      cz2+=coords2[i][2];
    }

    cx1/=(real)npoints;
    cy1/=(real)npoints;
    cz1/=(real)npoints;

    cx2/=(real)npoints;
    cy2/=(real)npoints;
    cz2/=(real)npoints;

    for (i=0; i<npoints; i++) {
      coords1[i][0]-=cx1;
      coords1[i][1]-=cy1;
      coords1[i][2]-=cz1;
      coords2[i][0]-=cx2;
      coords2[i][1]-=cy2;
      coords2[i][2]-=cz2;
    }

    for (i=0; i<ntpoints; i++) {
      tpoints[i][0]-=cx2;
      tpoints[i][1]-=cy2;
      tpoints[i][2]-=cz2;
    }

    for (i=0; i<3; i++)
      for (j=0; j<3; j++) {
        if (i==j)
          mat_s[i][j]=mat_a[i][j]=mat_b[i][j]=mat_g[i][j]=1.0;
        else
          mat_s[i][j]=mat_a[i][j]=mat_b[i][j]=mat_g[i][j]=0.0;
        mat_u[i][j]=0.;
      }

    for (n=0; n<npoints; n++) {
      mat_u[0][0]+=coords1[n][0]*coords2[n][0];
      mat_u[0][1]+=coords1[n][0]*coords2[n][1];
      mat_u[0][2]+=coords1[n][0]*coords2[n][2];
      mat_u[1][0]+=coords1[n][1]*coords2[n][0];
      mat_u[1][1]+=coords1[n][1]*coords2[n][1];
      mat_u[1][2]+=coords1[n][1]*coords2[n][2];
      mat_u[2][0]+=coords1[n][2]*coords2[n][0];
      mat_u[2][1]+=coords1[n][2]*coords2[n][1];
      mat_u[2][2]+=coords1[n][2]*coords2[n][2];
    }

    for (i=0; i<3; i++)
      for (j=0; j<3; j++)
        tmp_mat[i][j]=0.;

    do {
      d=mat_u[2][1]-mat_u[1][2];
      if (d==0) alpha=0; else alpha=atan(d/(mat_u[1][1]+mat_u[2][2]));
      if (cos(alpha)*(mat_u[1][1]+mat_u[2][2])+sin(alpha)*(mat_u[2][1]-mat_u[1][2])<0.0)       alpha+=M_PI;
      mat_a[1][1]=mat_a[2][2]=cos(alpha);
      mat_a[2][1]=sin(alpha);
      mat_a[1][2]=-mat_a[2][1];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++)
          for (k=0; k<3; k++)
            tmp_mat[i][j]+=mat_u[i][k]*mat_a[j][k];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++) {
          mat_u[i][j]=tmp_mat[i][j];
          tmp_mat[i][j]=0.;
        }
      for (i=0; i<3; i++)
        for (j=0; j<3; j++)
          for (k=0; k<3; k++)
            tmp_mat[i][j]+=mat_a[i][k]*mat_s[k][j];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++) {
          mat_s[i][j]=tmp_mat[i][j];
          tmp_mat[i][j]=0.;
        }
      d=mat_u[0][2]-mat_u[2][0];
      if (d==0) beta=0; else beta=atan(d/(mat_u[0][0]+mat_u[2][2]));
      if (cos(beta)*(mat_u[0][0]+mat_u[2][2])+sin(beta)*(mat_u[0][2]-mat_u[2][0])<0.0) beta+=M_PI;
      mat_b[0][0]=mat_b[2][2]=cos(beta);
      mat_b[0][2]=sin(beta);
      mat_b[2][0]=-mat_b[0][2];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++)
          for (k=0; k<3; k++)
            tmp_mat[i][j]+=mat_u[i][k]*mat_b[j][k];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++) {
          mat_u[i][j]=tmp_mat[i][j];
          tmp_mat[i][j]=0.;
        }
      for (i=0; i<3; i++)
        for (j=0; j<3; j++)
          for (k=0; k<3; k++)
            tmp_mat[i][j]+=mat_b[i][k]*mat_s[k][j];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++) {
          mat_s[i][j]=tmp_mat[i][j];
          tmp_mat[i][j]=0.;
        }
      d=mat_u[1][0]-mat_u[0][1];
      if (d==0) gamma=0; else gamma=atan(d/(mat_u[0][0]+mat_u[1][1]));
      if (cos(gamma)*(mat_u[0][0]+mat_u[1][1])+sin(gamma)*(mat_u[1][0]-mat_u[0][1])<0.0)
        gamma+=M_PI;
      mat_g[0][0]=mat_g[1][1]=cos(gamma);
      mat_g[1][0]=sin(gamma);
      mat_g[0][1]=-mat_g[1][0];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++)
          for (k=0; k<3; k++)
            tmp_mat[i][j]+=mat_u[i][k]*mat_g[j][k];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++) {
          mat_u[i][j]=tmp_mat[i][j];
          tmp_mat[i][j]=0.;
        }
      for (i=0; i<3; i++)
        for (j=0; j<3; j++)
          for (k=0; k<3; k++)
            tmp_mat[i][j]+=mat_g[i][k]*mat_s[k][j];
      for (i=0; i<3; i++)
        for (j=0; j<3; j++) {
          mat_s[i][j]=tmp_mat[i][j];
          tmp_mat[i][j]=0.;
        }
      val=fabs(alpha)+fabs(beta)+fabs(gamma);
    } while (val>0.001);

    val=0.;
    for (i=0; i<npoints; i++) {
      x=coords2[i][0];
      y=coords2[i][1];
      z=coords2[i][2];
      tmpx=x*mat_s[0][0]+y*mat_s[0][1]+z*mat_s[0][2];
      tmpy=x*mat_s[1][0]+y*mat_s[1][1]+z*mat_s[1][2];
      tmpz=x*mat_s[2][0]+y*mat_s[2][1]+z*mat_s[2][2];
      x=coords1[i][0]-tmpx;
      y=coords1[i][1]-tmpy;
      z=coords1[i][2]-tmpz;
      val+=x*x+y*y+z*z;
    }

    for (i=0; i<ntpoints; i++) {
      x=tpoints[i][0];
      y=tpoints[i][1];
      z=tpoints[i][2];
      tpoints[i][0]=x*mat_s[0][0]+y*mat_s[0][1]+z*mat_s[0][2];
      tpoints[i][1]=x*mat_s[1][0]+y*mat_s[1][1]+z*mat_s[1][2];
      tpoints[i][2]=x*mat_s[2][0]+y*mat_s[2][1]+z*mat_s[2][2];
    }

    for (i=0; i<npoints; i++) {
      coords1[i][0]+=cx1;
      coords1[i][1]+=cy1;
      coords1[i][2]+=cz1;
      coords2[i][0]+=cx2;
      coords2[i][1]+=cy2;
      coords2[i][2]+=cz2;
    }

    for (i=0; i<ntpoints; i++) {
      tpoints[i][0]+=cx1;
      tpoints[i][1]+=cy1;
      tpoints[i][2]+=cz1;
    }

  return sqrt(val/(real)npoints);
}


atom_type *find_atom(res_type *res, char *aname)
{
  atom_type *atom;

    atom = res->atoms;
    while (atom) {
      if (atom->name[0]==aname[0] && atom->name[1]==aname[1] && atom->name[2]==aname[2]) {
        return atom;
        break;
      }
      atom = atom->next;
    }

  return NULL;
}


void add_replace(res_type *res, char *aname, real x, real y, real z, int flags)
{
  atom_type *atom, *newatom;

    atom = res->atoms;
    while (atom) {
      if (atom->name[0]==aname[0] && atom->name[1]==aname[1] && atom->name[2]==aname[2]) {
        atom->x = x; atom->y = y; atom->z = z;
        atom->flag |= flags;
        break;
      }
      atom = atom->next;
    }

    if (!atom) {
      newatom = (atom_type*)calloc(sizeof(atom_type),1);
      newatom->x = x;
      newatom->y = y;
      newatom->z = z;
      newatom->flag |= flags;
      newatom->res = res;
      newatom->name = (char*)calloc(4,1);
      strcpy(newatom->name,aname);

      atom = res->atoms;
      while (atom) {
        if (atom->name[0]=='C' && atom->name[1]=='A')
          break;
        atom = atom->next;
      }
      if (aname[0]=='N' && aname[1]==' ') {
        newatom->next = res->atoms;
        res->atoms = newatom;
      } else {
        while (atom->next) atom=atom->next;
        atom->next = newatom;
      }
    }
}


int **RBINS;
real **X_COORDS, **C_ALPHA;

#ifdef COMPILE_BB

void rebuild_backbone(void)
{

  res_type *res, *prevres;
  atom_type *atom;
  real **cacoords, **tmpcoords, **tmpstat;
  real x1, y1, z1;
  real x2, y2, z2;
  real x3, y3, z3;
  real x4, y4, z4;
  real r13_1, r13_2, r14;
  real besthit, hit;
  int bestpos;
  int i, j, k, l, m, bin13_1, bin13_2, bin14, found, pro;
  int b13_1, b13_2, b14;
  real rmsd, total, maxrms;
  FILE *debug, *out;

    if (_VERBOSE) printf("Rebuilding backbone...\n");

    RBINS = (int**)calloc(sizeof(int*)*(chain_length+1),1);
    for (i=0;i<chain_length+1;i++)
      RBINS[i] = (int*)calloc(sizeof(int)*3,1);

    X_COORDS = (real**)calloc(sizeof(real*)*(chain_length+10),1);
    for (i=0;i<chain_length+10;i++)
      X_COORDS[i] = (real*)calloc(sizeof(real)*3,1);

    i = 5;

	res = chain->residua;
    while (res) {
      atom = res->atoms;
      while (atom) {
        if (atom->name[0]=='C' && atom->name[1]=='A') {
          X_COORDS[i][0] = atom->x;
          X_COORDS[i][1] = atom->y;
          X_COORDS[i][2] = atom->z;
          i++;
        }
        atom = atom->next;
      }
      res = res->next;
    }

    cacoords = (real**)calloc(sizeof(real*)*(8),1);
    tmpcoords = (real**)calloc(sizeof(real*)*(8),1);
    tmpstat = (real**)calloc(sizeof(real*)*(8),1);
    for (i=0;i<8;i++) {
      cacoords[i] = (real*)calloc(sizeof(real)*3,1);;
      tmpcoords[i] = (real*)calloc(sizeof(real)*3,1);;
      tmpstat[i] = (real*)calloc(sizeof(real)*3,1);;
    }

    C_ALPHA = &X_COORDS[5];

    // rebuild ends...

    for (i=0,j=0;i<5;i++,j++)
      for (k=0;k<3;k++)
        tmpcoords[j][k] = C_ALPHA[i][k];
    for (i=2,j=0;i<5;i++,j++)
      for (k=0;k<3;k++)
        cacoords[j][k] = C_ALPHA[i][k];
    for (i=0,j=0;i<3;i++,j++)
      for (k=0;k<3;k++)
        tmpstat[j][k] = C_ALPHA[i][k];

    superimpose2(tmpstat,cacoords,3,tmpcoords,5);

    for (i=-2,j=0;i<0;i++,j++)
      for (k=0;k<3;k++)
        C_ALPHA[i][k] = tmpcoords[j][k];

    for (i=chain_length-5,j=0;i<chain_length;i++,j++)
      for (k=0;k<3;k++)
        tmpcoords[j][k] = C_ALPHA[i][k];
    for (i=chain_length-5,j=0;i<chain_length-2;i++,j++)
      for (k=0;k<3;k++)
        cacoords[j][k] = C_ALPHA[i][k];
    for (i=chain_length-3,j=0;i<chain_length;i++,j++)
      for (k=0;k<3;k++)
        tmpstat[j][k] = C_ALPHA[i][k];

    superimpose2(tmpstat,cacoords,3,tmpcoords,5);

    for (i=chain_length-3,j=0;i<chain_length;i++,j++)
      for (k=0;k<3;k++)
        C_ALPHA[i+3][k] = tmpcoords[j+3][k];


    prevres = NULL;
    res = chain->residua;


    total = maxrms = 0.0;

    for (i=0;i<chain_length+1;i++) {
    	x1 = C_ALPHA[i-2][0];
    	y1 = C_ALPHA[i-2][1];
    	z1 = C_ALPHA[i-2][2];

    	x2 = C_ALPHA[i-1][0];
    	y2 = C_ALPHA[i-1][1];
    	z2 = C_ALPHA[i-1][2];

    	x3 = C_ALPHA[i][0];
    	y3 = C_ALPHA[i][1];
    	z3 = C_ALPHA[i][2];

    	x4 = C_ALPHA[i+1][0];
    	y4 = C_ALPHA[i+1][1];
    	z4 = C_ALPHA[i+1][2];

    	r13_1 = calc_distance(x1, y1, z1, x3, y3, z3);
    	r13_2 = calc_distance(x2, y2, z2, x4, y4, z4);
    	r14 = calc_r14(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4);

      bin13_1 = (int)((r13_1-4.6)/0.3);
      bin13_2 = (int)((r13_2-4.6)/0.3);
      bin14 = (int)((r14+11.)/0.3);

      if (bin13_1<0) bin13_1=0;
      if (bin13_2<0) bin13_2=0;
      if (bin14<0) bin14=0;
      if (bin13_1>9) bin13_1=9;
      if (bin13_2>9) bin13_2=9;
      if (bin14>73) bin14=73;

      RBINS[i][0] = bin13_1;
      RBINS[i][1] = bin13_2;
      RBINS[i][2] = bin14;

    	cacoords[0][0] = x1;
    	cacoords[0][1] = y1;
    	cacoords[0][2] = z1;

    	cacoords[1][0] = x2;
     	cacoords[1][1] = y2;
     	cacoords[1][2] = z2;

     	cacoords[2][0] = x3;
     	cacoords[2][1] = y3;
     	cacoords[2][2] = z3;

     	cacoords[3][0] = x4;
     	cacoords[3][1] = y4;
     	cacoords[3][2] = z4;

      pro = 0;

      if (prevres && !strncmp(prevres->name,"PRO",3)) {
        j=0;
        besthit=1000.;
        bestpos=0;
        do {
          hit = abs(nco_stat_pro[j].bins[0]-bin13_1)+abs(nco_stat_pro[j].bins[1]-bin13_2)+0.2*abs(nco_stat_pro[j].bins[2]-bin14);
          if (hit<besthit) {
            besthit=hit;
            bestpos=j;
          }
          j++;
        } while (nco_stat_pro[j].bins[0]>=0 && hit>1e-3);
        for (j=0;j<4;j++) {
         	for (k=0;k<3;k++) {
         		tmpstat[j][k] = nco_stat_pro[bestpos].data[j][k];
       	  }
     	  }
        for (j=0;j<8;j++) {
         	for (k=0;k<3;k++) {
         		tmpcoords[j][k] = nco_stat_pro[bestpos].data[j][k];
       	  }
     	  }
      } else {
        j=0;
        besthit=1000.;
        bestpos=0;
        do {
          hit = abs(nco_stat[j].bins[0]-bin13_1)+abs(nco_stat[j].bins[1]-bin13_2)+0.2*abs(nco_stat[j].bins[2]-bin14);
          if (hit<besthit) {
            besthit=hit;
            bestpos=j;
          }
          j++;
        } while (nco_stat[j].bins[0]>=0 && hit>1e-3);
        for (j=0;j<4;j++) {
         	for (k=0;k<3;k++) {
         		tmpstat[j][k] = nco_stat[bestpos].data[j][k];
       	  }
     	  }
        for (j=0;j<8;j++) {
         	for (k=0;k<3;k++) {
         		tmpcoords[j][k] = nco_stat[bestpos].data[j][k];
       	  }
     	  }
      }

     	rmsd=superimpose2(cacoords, tmpstat, 4, tmpcoords, 8);

     	total += rmsd;
     	if (rmsd>maxrms) maxrms=rmsd;

// add-or-replace

      if (prevres) {
        add_replace(prevres, "C  ", tmpcoords[4][0], tmpcoords[4][1], tmpcoords[4][2], FLAG_BACKBONE);
        add_replace(prevres, "O  ", tmpcoords[5][0], tmpcoords[5][1], tmpcoords[5][2], FLAG_BACKBONE);
      }

      if (res) {
        add_replace(res, "N  ", tmpcoords[6][0], tmpcoords[6][1], tmpcoords[6][2], FLAG_BACKBONE);
      }

      prevres = res;
      if (res)
        res = res->next;
    }

    if (_VERBOSE) printf("Backbone rebuilding deviation: average = %.3f, max = %.3f\n", total/(real)chain_length, maxrms);
}

#endif


#ifdef COMPILE_ROT

typedef struct _rot_struct {
  int r13_1, r13_2, r14;
  int nc;
  real ***coords;
  struct _rot_struct *next;
} rot_struct;

rot_struct *rotamers[20];

/* this is obsolete in a standalone version of PULCHRA */
void read_rotamers(void)
{
  FILE *inp;
  char buf[1000];
  char dum[100];
  int aa, i, j, k, l, n;
  rot_struct *new_rot, *last_rot;
  real x, y, z;

    if (_VERBOSE) printf("Reading rotamer library...\n");

    inp = fopen("NEWROT","r");
    last_rot=NULL;
    while (!feof(inp)) {
      if (fgets(buf,1000,inp)==buf) {
        if (buf[0]=='A') {
          sscanf(buf,"%s %d", dum, &aa);
          if (last_rot) last_rot->next = NULL;
          last_rot = NULL;
          if (fgets(buf,1000,inp)!=buf) break;
        }
//        printf("aa: %d\n", aa);
        if (aa==20) break;
        sscanf(buf,"%d %d %d %s %d", &i, &j, &k, dum, &l);
        new_rot = (rot_struct*)calloc(sizeof(rot_struct),1);
//        printf("%d %d %d nc: %d\n", i, j, k, l);
        new_rot->r13_1 = i;
        new_rot->r13_2 = j;
        new_rot->r14 = k;
        new_rot->nc = l;
        new_rot->next = NULL;
        new_rot->coords = (real***)calloc(sizeof(real**)*l,1);
        for (i=0;i<l;i++) {
          new_rot->coords[i]=(real**)calloc(sizeof(real*)*(nheavy[aa]+1),1);
          for (j=0;j<(nheavy[aa]+1);j++) {
            new_rot->coords[i][j]=(real*)calloc(sizeof(real)*3,1);
          }
        }
        for (i=0;i<l;i++) {
          fgets(buf,1000,inp);
          for (j=0;j<(nheavy[aa]+1);j++) {
            fgets(buf,1000,inp);
            sscanf(buf,"%lf%lf%lf",&x, &y, &z);
            new_rot->coords[i][j][0]=x;
            new_rot->coords[i][j][1]=y;
            new_rot->coords[i][j][2]=z;
          }
          if (last_rot) {
            last_rot->next = new_rot;
          } else {
            rotamers[aa] = new_rot;
          }
          last_rot = new_rot;
        }
      }
    }
    fclose(inp);
}


void cross(real *v1, real *v2, real *v3)
{
  v3[0] = v1[1]*v2[2]-v1[2]*v2[1];
  v3[1] = v1[2]*v2[0]-v1[0]*v2[2];
  v3[2] = v1[0]*v2[1]-v1[1]*v2[0];
}

void norm(real *v)
{
  real d;

    d = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
    v[0] /= d;
    v[1] /= d;
    v[2] /= d;
}


int check_xvol(res_type *res)
{
  res_type *res2;
  atom_type *atom1, *atom2;
  real dx, dy, dz, dd;

    res2 = chain->residua;

    while (res2) {
      atom2 = res2->atoms;
      if (res!=res2) {
        while (atom2) {
          atom1 = res->atoms;
          while (atom1) {
            if (atom1->flag & FLAG_SIDECHAIN) {
              dx = atom1->x-atom2->x;
              dy = atom1->y-atom2->y;
              dz = atom1->z-atom2->z;
              dd = dx*dx+dy*dy+dz*dz;
              if (dd<(1.7*1.7)) {
                return 1;
              }
            }
            atom1=atom1->next;
          }
          atom2=atom2->next;
        }
      }
      res2=res2->next;
    }

  return 0;
}


real ***SORTED_ROTAMERS;

void rebuild_sidechains(void)
{
  FILE *out;
  res_type *res, *prevres, *testres;
  atom_type *atom, *atom1, *atom2;
  real **cacoords, **tmpcoords, **tmpstat;
  real x1, y1, z1;
  real x2, y2, z2;
  real x3, y3, z3;
  real x4, y4, z4;
  real x5, y5, z5;
  real r14, r13_1, r13_2;
  real dx, dy, dz, dd;
  real hit, besthit;
  int exvol, bestpos;
  int i, j, k, l, m, bin13_1, bin13_2, bin14;
  real rmsd, total;
  real v1[3], v2a[3], v2b[3], v2[3], v3[3];
  int nsc, nca;
  real cax, cay, caz;
  real **lsys, **vv, **sc;
  char scn[12][4];
  rot_struct *rot;
  int ok, last_a, last_b, last_c, last_d, jpos;
  int jx, jy, jz, jxi, jyi, jzi, b13_1, b13_2, b14, jm;
  int crot, bestrot, minexvol, totexvol, rtried, pos, cpos;
  real cmx, cmy, cmz, ddx, ddy, ddz, ddd, bestdd;
  real sort_rot[100][2];

    if (_VERBOSE) printf("Rebuilding side chains...\n");

    lsys = (real**)calloc(sizeof(real*)*3,1);
    vv = (real**)calloc(sizeof(real*)*3,1);
    sc = (real**)calloc(sizeof(real*)*12,1);
    for (i=0;i<12;i++)
      sc[i] = (real*)calloc(sizeof(real)*3,1);
    for (i=0;i<3;i++) {
      lsys[i] = (real*)calloc(sizeof(real)*3,1);
      vv[i] = (real*)calloc(sizeof(real)*3,1);
    }

    SORTED_ROTAMERS = (real***)calloc(sizeof(real**)*(chain_length+1),1);
    for (i=0;i<chain_length+1;i++) {
      SORTED_ROTAMERS[i] = (real**)calloc(sizeof(real*)*10,1);
      for (j=0;j<10;j++) {
        SORTED_ROTAMERS[i][j] = (real*)calloc(sizeof(real)*2,1);
      }
    }

    prevres = NULL;
    res = chain->residua;
    totexvol = 0;

    for (i=0;i<chain_length;i++) {
      if (!strncmp(res->name,"GLY",3) || !res->protein) {
        if (res->next) res = res->next;
        continue;
      }

  		x1 = C_ALPHA[i-2][0];
  		y1 = C_ALPHA[i-2][1];
  		z1 = C_ALPHA[i-2][2];
  		x2 = C_ALPHA[i-1][0];
  		y2 = C_ALPHA[i-1][1];
  		z2 = C_ALPHA[i-1][2];
  		x3 = C_ALPHA[i][0];
  		y3 = C_ALPHA[i][1];
  		z3 = C_ALPHA[i][2];
  		x4 = C_ALPHA[i+1][0];
  		y4 = C_ALPHA[i+1][1];
  		z4 = C_ALPHA[i+1][2];

      bin13_1 = RBINS[i][0];
      bin13_2 = RBINS[i][1];
      bin14 = RBINS[i][2];

      v1[0] = x4-x2;
      v1[1] = y4-y2;
      v1[2] = z4-z2;

      v2a[0] = x4-x3;
      v2a[1] = y4-y3;
      v2a[2] = z4-z3;

      v2b[0] = x3-x2;
      v2b[1] = y3-y2;
      v2b[2] = z3-z2;

      cross(v2a, v2b, v2);
      cross(v1, v2, v3);

      norm(v1);
      norm(v2);
      norm(v3);

// gather 10 closest rotamer conformations...

      for (j=0;j<10;j++)
        SORTED_ROTAMERS[i][j][0] = 500.;

      j = 0;
      besthit = 1000.;
      bestpos = 0;
      do {
        if (rot_stat_idx[j][0]==res->type) {
          hit = abs(rot_stat_idx[j][1]-bin13_1)+abs(rot_stat_idx[j][2]-bin13_2)+0.2*abs(rot_stat_idx[j][3]-bin14);
          if (hit<SORTED_ROTAMERS[i][9][0]) {
            k = 9;
            while (k>=0 && hit<SORTED_ROTAMERS[i][k][0]) {
              k--;
            }
            k++;
            // k = hit
            for (l=9;l>k;l--) {
              SORTED_ROTAMERS[i][l][0]=SORTED_ROTAMERS[i][l-1][0];
              SORTED_ROTAMERS[i][l][1]=SORTED_ROTAMERS[i][l-1][1];
            }
            SORTED_ROTAMERS[i][k][0]=hit;
            SORTED_ROTAMERS[i][k][1]=j;
          }
        }
        j++;
      } while (rot_stat_idx[j][0]>=0);


      besthit = SORTED_ROTAMERS[i][0][0];
      bestpos = SORTED_ROTAMERS[i][0][1];


// new rebuild...

      pos = rot_stat_idx[bestpos][5];
      nsc = nheavy[res->type]+1;

      if (_PDB_SG) { // more than one rotamer - check SC
        bestdd = 100.; crot = 0;
        for (l=0;l<2;l++) { // check two closest conformations
          cpos = SORTED_ROTAMERS[i][l][1];
          for (m=0;m<rot_stat_idx[cpos][4];m++) {
            for (j=0;j<3;j++) {
              vv[0][j] = v1[j]; vv[1][j] = v2[j]; vv[2][j] = v3[j];
              for (k=0;k<3;k++) {
                if (j==k) lsys[j][k]=1.; else lsys[j][k]=0.;
              }
            }
            pos = rot_stat_idx[cpos][5]+nsc*m;
            for (j=0;j<nsc;j++) {
              for (k=0;k<3;k++) {
                sc[j][k] = rot_stat_coords[pos+j][k];
              }
            }
            superimpose2(vv,lsys,3,sc,nsc);
            for (j=0;j<nsc;j++) {
              sc[j][0] += x3;
              sc[j][1] += y3;
              sc[j][2] += z3;
            }
            cmx = 0.; cmy = 0.; cmz = 0.;
            for (j=0;j<nsc;j++) {
              cmx += sc[j][0];
              cmy += sc[j][1];
              cmz += sc[j][2];
            }
            cmx /= (real) nsc;
            cmy /= (real) nsc;
            cmz /= (real) nsc;
            ddx = res->cmx-cmx;
            ddy = res->cmy-cmy;
            ddz = res->cmz-cmz;
            ddx *= ddx;
            ddy *= ddy;
            ddz *= ddz;
            ddd = ddx+ddy+ddz;
            if (ddd<bestdd) {
              bestdd = ddd;
              crot = pos; // closest rotamer position
            }
          }
        }
        pos = crot;
      } // PDB_SG

      for (j=0;j<3;j++) {
        vv[0][j] = v1[j]; vv[1][j] = v2[j]; vv[2][j] = v3[j];
        for (k=0;k<3;k++) {
          if (j==k) lsys[j][k]=1.; else lsys[j][k]=0.;
        }
      }

      for (j=0;j<nsc;j++) {
        for (k=0;k<3;k++) {
          sc[j][k] = rot_stat_coords[pos+j][k];
        }
      }

      superimpose2(vv,lsys,3,sc,nsc);

      for (j=0;j<nsc;j++) {
        sc[j][0] += x3;
        sc[j][1] += y3;
        sc[j][2] += z3;
      }

      for (j=1;j<nsc;j++) {
        add_replace(res, heavy_atoms[10*res->type+j-1], sc[j][0], sc[j][1], sc[j][2], FLAG_SIDECHAIN);
      }

      if (res->next) res = res->next;

    } // i++, next res

    for (i=0;i<12;i++)
      free(sc[i]);
    for (i=0;i<3;i++) {
      free(lsys[i]);
      free(vv[i]);
    }
    free(sc);
	free(lsys);
	free(vv);
}


typedef struct _atom_list {
  atom_type *atom;
  struct _atom_list *next;
} atom_list;

int get_conflicts(res_type *res, atom_list ****grid, int xgrid, int ygrid, int zgrid)
{
  atom_list *llist;
  atom_type *atom, *atom2;
  int i, j, k, x, y, z;
  int ii, jj, kk, con, iter, maxcon, merged;
  real dx, dy, dz, dd;

    con = 0;
    atom = res->atoms;
    while (atom) {
      i = atom->gx;
      j = atom->gy;
      k = atom->gz;
      for (ii=i-2;ii<=i+2;ii++)
        for (jj=j-2;jj<=j+2;jj++)
          for (kk=k-2;kk<=k+2;kk++) {
            if (ii>=0 && ii<xgrid && jj>=0 && jj<ygrid && kk>=0 && kk<zgrid) {
              llist = grid[ii][jj][kk];
              while (llist) {
                atom2 = llist->atom;
                if (atom && atom2 && res && atom2->res) {
                  merged=0;
                  if (res==atom2->res) { // self-xvol
                    if (atom->flag & FLAG_SIDECHAIN && atom2->flag & FLAG_SIDECHAIN) merged=1;
                    if (atom->flag & FLAG_BACKBONE && atom2->flag & FLAG_BACKBONE) merged=1;
                    if (atom->name[0]=='C' && atom->name[1]=='A' && atom2->name[0]=='C' && atom2->name[1]=='B') merged=1;
                    if (atom->name[0]=='C' && atom->name[1]=='B' && atom2->name[0]=='C' && atom2->name[1]=='A') merged=1;
                    if (res->name[0]=='P') {
                      if (atom->name[0]=='C' && atom->name[1]=='D' && atom2->name[0]=='N' && atom2->name[1]==' ') merged=1;
                      if (atom->name[0]=='N' && atom->name[1]==' ' && atom2->name[0]=='C' && atom2->name[1]=='D') merged=1;
                    }

                    if (!merged) {
//                      printf("merged: %s[%d] %s-%s %d %d\n", res->name,res->num,atom->name,atom2->name,atom->flag,atom2->flag);
                    }
                  } else
                  if (res->next==atom2->res || res==atom2->res->next) {
                    if (atom->name[0]=='C' && atom->name[1]==' ' && atom2->name[0]=='N' && atom2->name[1]==' ') merged=1;
                    if (atom->name[0]=='N' && atom->name[1]==' ' && atom2->name[0]=='C' && atom2->name[1]==' ') merged=1;
                  }
                  if (atom->flag & FLAG_BACKBONE && atom2->flag & FLAG_BACKBONE) merged=1; // for now
                  if (atom->flag & FLAG_SCM || atom2->flag & FLAG_SCM) merged=1; // for now
                  if (!merged) {
                    dx = atom->x-atom2->x;
                    dx*=dx;
                    dy = atom->y-atom2->y;
                    dy*=dy;
                    dz = atom->z-atom2->z;
                    dz*=dz;
                    dd = dx+dy+dz;
                    if (dd<_SG_XVOL_DIST*_SG_XVOL_DIST) {
                      con++;
                    }
                  }
                }
                llist = llist->next;
              }
            }
          }
      atom = atom->next;
    }

  return con;
}

int display_conflicts(res_type *res, atom_list ****grid, int xgrid, int ygrid, int zgrid)
{
  atom_list *llist;
  atom_type *atom, *atom2;
  int i, j, k, x, y, z;
  int ii, jj, kk, con, iter, maxcon, merged;
  real dx, dy, dz, dd;

    con = 0;
    atom = res->atoms;
    while (atom) {
      i = atom->gx;
      j = atom->gy;
      k = atom->gz;
      for (ii=i-2;ii<=i+2;ii++)
        for (jj=j-2;jj<=j+2;jj++)
          for (kk=k-2;kk<=k+2;kk++) {
            if (ii>=0 && ii<xgrid && jj>=0 && jj<ygrid && kk>=0 && kk<zgrid) {
              llist = grid[ii][jj][kk];
              while (llist) {
                atom2 = llist->atom;
                if (atom && atom2 && res && atom2->res) {
                  merged=0;
                  if (res==atom2->res) { // self-xvol
                    if (atom->flag & FLAG_SIDECHAIN && atom2->flag & FLAG_SIDECHAIN) merged=1;
                    if (atom->flag & FLAG_BACKBONE && atom2->flag & FLAG_BACKBONE) merged=1;
                    if (atom->name[0]=='C' && atom->name[1]=='A' && atom2->name[0]=='C' && atom2->name[1]=='B') merged=1;
                    if (atom->name[0]=='C' && atom->name[1]=='B' && atom2->name[0]=='C' && atom2->name[1]=='A') merged=1;
                    if (res->name[0]=='P') {
                      if (atom->name[0]=='C' && atom->name[1]=='D' && atom2->name[0]=='N' && atom2->name[1]==' ') merged=1;
                      if (atom->name[0]=='N' && atom->name[1]==' ' && atom2->name[0]=='C' && atom2->name[1]=='D') merged=1;
                    }
                    if (!merged) {
//                      printf("merged: %s[%d] %s-%s %d %d\n", res->name,res->num,atom->name,atom2->name,atom->flag,atom2->flag);
                    }
                  } else
                  if (res->next==atom2->res || res==atom2->res->next) {
                    if (atom->name[0]=='C' && atom->name[1]==' ' && atom2->name[0]=='N' && atom2->name[1]==' ') merged=1;
                    if (atom->name[0]=='N' && atom->name[1]==' ' && atom2->name[0]=='C' && atom2->name[1]==' ') merged=1;
                  }
                  if (atom->flag & FLAG_BACKBONE && atom2->flag & FLAG_BACKBONE) merged=1; // for now
                  if (atom->flag & FLAG_SCM || atom2->flag & FLAG_SCM) merged=1; // for now
                  if (!merged) {
                    dx = atom->x-atom2->x;
                    dx*=dx;
                    dy = atom->y-atom2->y;
                    dy*=dy;
                    dz = atom->z-atom2->z;
                    dz*=dz;
                    dd = dx+dy+dz;
                    if (dd<1.6*1.6) {
                      printf("STERIC CONFLICT: %s[%d]%s-%s[%d]%s\n", atom->res->name,atom->res->num,atom->name,atom2->res->name,atom2->res->num,atom2->name);
                      con++;
                    }
                  }
                }
                llist = llist->next;
              }
            }
          }
      atom = atom->next;
    }

  return con;
}


void allocate_grid(atom_list *****grid_, int *xgrid_, int *ygrid_, int *zgrid_)
{
  static int xgrid, ygrid, zgrid;
  static atom_list ****grid = NULL;
  atom_list *llist, *alist;
  real min[3], max[3];
  res_type *res, *worst;
  atom_type *atom, *atom2;
  int i, j, x, y, z;

    if (!grid && chain->residua && chain->residua->atoms) {
 	    res = chain->residua;
      min[0]=max[0]=res->atoms->x;
      min[1]=max[1]=res->atoms->y;
      min[2]=max[2]=res->atoms->z;
	    while (res) {
	      atom = res->atoms;
	      while (atom) {
	        if (atom->x<min[0]) min[0]=atom->x;
	        if (atom->y<min[1]) min[1]=atom->y;
	        if (atom->z<min[2]) min[2]=atom->z;
	        if (atom->x>max[0]) max[0]=atom->x;
	        if (atom->y>max[1]) max[1]=atom->y;
	        if (atom->z>max[2]) max[2]=atom->z;
	        atom = atom->next;
	      }
	      res = res->next;
	    }

	    xgrid = (max[0]-min[0])/GRID_RES;
	    ygrid = (max[1]-min[1])/GRID_RES;
	    zgrid = (max[2]-min[2])/GRID_RES;

	    if (_VERBOSE) printf("Allocating grid (%d %d %d)...\n", xgrid, ygrid, zgrid);

	   grid = (atom_list****)calloc(sizeof(atom_list***)*(xgrid+1),1);
	   for (i=0;i<xgrid+1;i++) {
	     grid[i] = (atom_list***)calloc(sizeof(atom_list**)*(ygrid+1),1);
	     for (j=0;j<ygrid+1;j++) {
	       grid[i][j] = (atom_list**)calloc(sizeof(atom_list*)*(zgrid+1),1);
	     }
	   }

	   res = chain->residua;
	   while (res) {
	     atom = res->atoms;
	     while (atom) {
	       x = xgrid*(atom->x-min[0])/(max[0]-min[0]);
	       y = ygrid*(atom->y-min[1])/(max[1]-min[1]);
	       z = zgrid*(atom->z-min[2])/(max[2]-min[2]);
	       alist = (atom_list*)calloc(sizeof(atom_list),1);
	       alist->atom = atom;
	       atom->gx = x;
	       atom->gy = y;
	       atom->gz = z;
	       if (grid[x][y][z]!=NULL) {
	         llist = grid[x][y][z];
	         while (llist->next) llist=llist->next;
	         llist->next = alist;
	       } else {
	         grid[x][y][z]=alist;
	       }
	       atom = atom->next;
	     }
	     res = res->next;
	   }
	} else {
	   if (_VERBOSE) printf("Grid already allocated (%d %d %d)\n", xgrid, ygrid, zgrid);
	}

	*grid_ = grid;
	*xgrid_ = xgrid;
	*ygrid_ = ygrid;
	*zgrid_ = zgrid;
}

void optimize_exvol(void)
{
  real min[3], max[3];
  res_type *res, *worst;
  atom_type *atom, *atom2;
  int xgrid, ygrid, zgrid;
  atom_list ****grid, *llist, *alist;
  int i, j, k, l, m, x, y, z;
  int ii, jj, kk, con, iter, maxcon, totcon;
  int cpos, bestpos, pos, con0;
  real v1[3], v2a[3], v2b[3], v2[3], v3[3];
  int nsc, nca;
  real cax, cay, caz;
  real **lsys, **vv, **sc;
  real x1, y1, z1;
  real x2, y2, z2;
  real x3, y3, z3;
  real x4, y4, z4;

    min[0]=1e5;
    min[1]=1e5;
    min[2]=1e5;
    max[0]=-1e5;
    max[1]=-1e5;
    max[2]=-1e5;

    lsys = (real**)calloc(sizeof(real*)*3,1);
    vv = (real**)calloc(sizeof(real*)*3,1);
    sc = (real**)calloc(sizeof(real*)*12,1);
    for (i=0;i<12;i++)
      sc[i] = (real*)calloc(sizeof(real)*3,1);
    for (i=0;i<3;i++) {
      lsys[i] = (real*)calloc(sizeof(real)*3,1);
      vv[i] = (real*)calloc(sizeof(real)*3,1);
    }

	allocate_grid(&grid, &xgrid, &ygrid, &zgrid);

    if (_VERBOSE) printf("Finding excluded volume conflicts...\n");

	iter = 0;

	do {
//printf("ITER: %d\n", iter);

      maxcon = 0;
      totcon=0;

      res = chain->residua;
      while (res) {
        if (res->protein) {
          con = get_conflicts(res, grid, xgrid, ygrid, zgrid);
          if (con>0) {
            totcon+=con;
            if (con>maxcon) {
              maxcon = con;
              worst = res;
            }
          }
        }
        res = res->next;
      }

      if (_VERBOSE && iter==0) {
        printf("Total number of conflicts: %d\n", totcon);
      }

      if (totcon==0) break;

      if (_VERBOSE && iter==0) {
        printf("Maximum number of conflicts: %s[%d] : %d\n", worst->name, worst->num, maxcon);
      }

      totcon=0;

      if (maxcon>0) {

// try to fix...

    res = chain->residua;
    for (i=0;i<chain_length;i++) {
      if (!strncmp(res->name,"GLY",3) || !res->protein) {
        if (res->next) res = res->next;
        continue;
      }

      nsc = nheavy[res->type]+1;

    	x1 = C_ALPHA[i-2][0];
    	y1 = C_ALPHA[i-2][1];
    	z1 = C_ALPHA[i-2][2];
    	x2 = C_ALPHA[i-1][0];
    	y2 = C_ALPHA[i-1][1];
    	z2 = C_ALPHA[i-1][2];
    	x3 = C_ALPHA[i][0];
    	y3 = C_ALPHA[i][1];
    	z3 = C_ALPHA[i][2];
    	x4 = C_ALPHA[i+1][0];
    	y4 = C_ALPHA[i+1][1];
    	z4 = C_ALPHA[i+1][2];

      v1[0] = x4-x2;
      v1[1] = y4-y2;
      v1[2] = z4-z2;

      v2a[0] = x4-x3;
      v2a[1] = y4-y3;
      v2a[2] = z4-z3;

      v2b[0] = x3-x2;
      v2b[1] = y3-y2;
      v2b[2] = z3-z2;

      cross(v2a, v2b, v2);
      cross(v1, v2, v3);

      norm(v1);
      norm(v2);
      norm(v3);

      con = get_conflicts(res, grid, xgrid, ygrid, zgrid);

      if (con>0) {

        bestpos=0;
        con0 = 100;
        for (l=0;l<10;l++) { // check two closest conformations
          cpos = SORTED_ROTAMERS[i][l][1];
          for (m=0;m<rot_stat_idx[cpos][4];m++) {
            for (j=0;j<3;j++) {
              vv[0][j] = v1[j]; vv[1][j] = v2[j]; vv[2][j] = v3[j];
              for (k=0;k<3;k++) {
                if (j==k) lsys[j][k]=1.; else lsys[j][k]=0.;
              }
            }
            pos = rot_stat_idx[cpos][5]+nsc*m;
            for (j=0;j<nsc;j++) {
              for (k=0;k<3;k++) {
                sc[j][k] = rot_stat_coords[pos+j][k];
              }
            }
            superimpose2(vv,lsys,3,sc,nsc);
            for (j=0;j<nsc;j++) {
              sc[j][0] += x3;
              sc[j][1] += y3;
              sc[j][2] += z3;
            }
            for (j=1;j<nsc;j++) {
              add_replace(res, heavy_atoms[10*res->type+j-1], sc[j][0], sc[j][1], sc[j][2], FLAG_SIDECHAIN);
            }
            con = get_conflicts(res, grid, xgrid, ygrid, zgrid);
//printf("test: %d\n", con);

            if (con<con0) {
              con0 = con;
              bestpos = pos;
            }
            if (con==0) break;
          }
          if (con==0) break;
        }

		totcon += con0;

        for (j=0;j<3;j++) {
          vv[0][j] = v1[j]; vv[1][j] = v2[j]; vv[2][j] = v3[j];
          for (k=0;k<3;k++) {
            if (j==k) lsys[j][k]=1.; else lsys[j][k]=0.;
          }
        }
        pos = bestpos;
        for (j=0;j<nsc;j++) {
          for (k=0;k<3;k++) {
            sc[j][k] = rot_stat_coords[pos+j][k];
          }
        }
        superimpose2(vv,lsys,3,sc,nsc);
        for (j=0;j<nsc;j++) {
          sc[j][0] += x3;
          sc[j][1] += y3;
          sc[j][2] += z3;
        }
        for (j=1;j<nsc;j++) {
          add_replace(res, heavy_atoms[10*res->type+j-1], sc[j][0], sc[j][1], sc[j][2], FLAG_SIDECHAIN);
        }
      }



      	res=res->next;

    	} // i
	}

	iter++;

	} while (iter<_XVOL_ITER);


    if (_VERBOSE) {
      if (totcon>0)
        printf("WARNING: %d steric conflict(s) are still there.\n", totcon);
      else
        printf("All steric conflicts removed.\n");
    }

    for (i=0;i<12;i++)
      free(sc[i]);
    for (i=0;i<3;i++) {
      free(lsys[i]);
      free(vv[i]);
    }
    free(sc);
	free(lsys);
	free(vv);


}


void vcross(real ax,real ay,real az,real bx,real by,real bz,real *cx,real *cy,real *cz)
{
    *cx = ay * bz - by * az;
    *cy = az * bx - bz * ax;
    *cz = ax * by - bx * ay;
}

real vdot(real ax,real ay,real az,real bx,real by,real bz)
{
    return ax*bx+ay*by+az*bz;
}

real calc_torsion(atom_type *a1, atom_type *a2, atom_type *a3, atom_type *a4)
{
  real v12x, v12y, v12z;
  real v43x, v43y, v43z;
  real zx, zy, zz;
  real px, py, pz;
  real xx, xy, xz;
  real yx, yy, yz;
  real u, v, angle;

    v12x = a1->x-a2->x;
    v12y = a1->y-a2->y;
    v12z = a1->z-a2->z;

    v43x = a4->x-a3->x;
    v43y = a4->y-a3->y;
    v43z = a4->z-a3->z;

    zx = a2->x-a3->x;
    zy = a2->y-a3->y;
    zz = a2->z-a3->z;

    vcross(zx,zy,zz,v12x,v12y,v12z,&px,&py,&pz);
    vcross(zx,zy,zz,v43x,v43y,v43z,&xx,&xy,&xz);
    vcross(zx,zy,zz,xx,xy,xz,&yx,&yy,&yz);

    u = vdot(xx,xy,xz,xx,xy,xz);
    v = vdot(yx,yy,yz,yx,yy,yz);

    angle = 360.;

    if (u<0. || v<0.) return angle;

    u = vdot(px,py,pz,xx,xy,xz) / sqrt(u);
    v = vdot(px,py,pz,yx,yy,yz) / sqrt(v);

    if (u != 0.0 || v != 0.0) angle = atan2(v, u) * RADDEG;


  return angle;

}


// Ca-N-C-Cb angle should be close to 34 deg

void chirality_check(void)
{
  int i;
  atom_type *a_ca, *a_n, *a_c, *a_cb;
  atom_type *atom;
  res_type *res;
  real angle;
  real nx, ny, nz;
  real px, py, pz;
  real qx, qy, qz;
  real rx, ry, rz;
  real xx, xy, xz;
  real yx, yy, yz;
  real dd, costheta, sintheta;

    if (_VERBOSE) printf("Checking chirality...\n");
    res = chain->residua;
    while (res) {
      a_ca = a_n = a_c = a_cb = NULL;
      a_ca = find_atom(res,"CA ");
      a_n = find_atom(res,"N  ");
      a_c = find_atom(res,"C  ");
      a_cb = find_atom(res,"CB ");
      if (a_ca && a_n && a_c && a_cb) {
        angle = calc_torsion(a_ca, a_n, a_c, a_cb);
        if (angle<0.) {
          if (_VERBOSE) printf("WARNING: D-aa detected at %s %3d : %5.2f", res->name, res->num, angle);
          xx = a_ca->x-a_n->x;
          xy = a_ca->y-a_n->y;
          xz = a_ca->z-a_n->z;
          yx = a_c->x-a_ca->x;
          yy = a_c->y-a_ca->y;
          yz = a_c->z-a_ca->z;
          vcross(xx,xy,xz,yx,yy,yz,&nx,&ny,&nz);
          dd = sqrt(nx*nx+ny*ny+nz*nz);
          nx /= dd;
          ny /= dd;
          nz /= dd;
          // nx, ny, nz = reflection plane normal
          rx = xx-yx;
          ry = xy-yy;
          rz = xz-yz;
          dd = sqrt(rx*rx+ry*ry+rz*rz);
          rx /= dd;
          ry /= dd;
          rz /= dd;
          costheta = -1.;
          sintheta = 0.;
          atom = res->atoms;
          while (atom) {
            if (atom->flag & FLAG_SIDECHAIN) {
              px = atom->x-a_ca->x;
              py = atom->y-a_ca->y;
              pz = atom->z-a_ca->z;
              qx = qy = qz = 0.;
              qx += (costheta + (1 - costheta) * rx * rx) * px;
              qx += ((1 - costheta) * rx * ry - rz * sintheta) * py;
              qx += ((1 - costheta) * rx * rz + ry * sintheta) * pz;
              qy += ((1 - costheta) * rx * ry + rz * sintheta) * px;
              qy += (costheta + (1 - costheta) * ry * ry) * py;
              qy += ((1 - costheta) * ry * rz - rx * sintheta) * pz;
              qz += ((1 - costheta) * rx * rz - ry * sintheta) * px;
              qz += ((1 - costheta) * ry * rz + rx * sintheta) * py;
              qz += (costheta + (1 - costheta) * rz * rz) * pz;
              qx += a_ca->x;
              qy += a_ca->y;
              qz += a_ca->z;
              atom->x = qx;
              atom->y = qy;
              atom->z = qz;
            }
            atom = atom->next;
          }
          angle = calc_torsion(a_ca, a_n, a_c, a_cb);
          if (_VERBOSE) printf(", fixed : %5.2f\n", angle);
        }
      }
      res = res->next;
    }
}


#endif

real hb_energy(res_type *res, atom_list ****grid, int xgrid, int ygrid, int zgrid)
{
	atom_type *atom, *c_atom1, *o_atom1, *n_atom1, *c_atom2, *o_atom2, *n_atom2, *tmp_atom;
	atom_type h_atom;
	int i, j, k, ii, jj, kk;
  atom_list *llist, *alist;
  real dx, dy, dz, dist, min_dist1, min_dist2;
  real hx1, hy1, hz1, dd;
  real dno, dnc, dho, dhc;
  real ene, Q;

    ene = 1e3;

    if (!res || !res->prev) return ene;
            
    Q = -27888.0; // DSSP h-bond energy constant

		c_atom1 = o_atom1 = n_atom1 = NULL;

  	atom = res->prev->atoms;
  	while (atom) {
			if (atom->name[0]=='C' && atom->name[1]==' ') c_atom1 = atom;
			if (atom->name[0]=='O' && atom->name[1]==' ') o_atom1 = atom;
			atom = atom->next;
		}

  	atom = res->atoms;
  	while (atom) {
			if (atom->name[0]=='N' && atom->name[1]==' ') { n_atom1 = atom; break; }
			atom = atom->next;
		}

// first bond

    min_dist2 = 1e10;
    o_atom2 = c_atom2 = NULL;
		if (n_atom1) {
			i = n_atom1->gx;
			j = n_atom1->gy;
			k = n_atom1->gz;
			for (ii=i-1;ii<=i+1;ii++) {
				for (jj=j-1;jj<=j+1;jj++) {
					for (kk=k-1;kk<=k+1;kk++) {
						if (ii>=0 && ii<xgrid && jj>=0 && jj<ygrid && kk>=0 && kk<=zgrid) {
							llist = grid[ii][jj][kk];
							while (llist) {
							  if (llist->atom->name[0]=='O' && llist->atom->name[1]==' ' && abs(llist->atom->res->locnum-n_atom1->res->locnum)>2)  {
							    tmp_atom = llist->atom;
							    dx = n_atom1->x-tmp_atom->x;
							    dy = n_atom1->y-tmp_atom->y;
							    dz = n_atom1->z-tmp_atom->z;
							    dist = dx*dx+dy*dy+dz*dz;
							    if (dist<min_dist2 && dist<25.0) {
							      o_atom2=tmp_atom;
							      min_dist2 = dist;
   						    } 
							  } 
							  llist = llist->next;
		          }					
						}
					}
				}
			}
		}

    if (o_atom2) {
      atom = o_atom2->res->atoms;
    	while (atom) {
  			if (atom->name[0]=='C' && atom->name[1]==' ') { c_atom2 = atom; break; }
  			atom = atom->next;
  		}                      
      if (c_atom2) {    
    		hx1 = o_atom1->x-c_atom1->x;
    		hy1 = o_atom1->y-c_atom1->y;
    		hz1 = o_atom1->z-c_atom1->z;
    		dd = -1.081f/sqrt(hx1*hx1+hy1*hy1+hz1*hz1);
    		hx1 *= dd;
    		hy1 *= dd;
    		hz1 *= dd;
    		
    		hx1 += n_atom1->x;
    		hy1 += n_atom1->y;
    		hz1 += n_atom1->z;
        
        add_replace(n_atom1->res, "H  ", hx1, hy1, hz1, FLAG_BACKBONE);

  // dno
        dx = n_atom1->x-o_atom2->x;
        dy = n_atom1->y-o_atom2->y;
        dz = n_atom1->z-o_atom2->z;
        dno = sqrt(dx*dx+dy*dy+dz*dz);
  
  // dnc
        dx = n_atom1->x-c_atom2->x;
        dy = n_atom1->y-c_atom2->y;
        dz = n_atom1->z-c_atom2->z;
        dnc = sqrt(dx*dx+dy*dy+dz*dz);
  
  // dho
        dx = hx1-o_atom2->x;
        dy = hy1-o_atom2->y;
        dz = hz1-o_atom2->z;
        dho = sqrt(dx*dx+dy*dy+dz*dz);
  
  // dhc
        dx = hx1-c_atom2->x;
        dy = hy1-c_atom2->y;
        dz = hz1-c_atom2->z;
        dhc = sqrt(dx*dx+dy*dy+dz*dz);
        
        if (dho<0.01F || dhc<0.01F || dnc<0.01F || dno<0.01F) {
          ene = -10.0;
        } else {
          ene = 0.001*(Q/dho - Q/dhc + Q/dnc - Q/dno);
        }       
      }
    }

/******
// second bond

    min_dist2 = 1e10;
    n_atom2 = NULL;
		if (n_atom1) {
			i = o_atom1->gx;
			j = o_atom1->gy;
			k = o_atom1->gz;
			for (ii=i-1;ii<=i+1;ii++) {
				for (jj=j-1;jj<=j+1;jj++) {
					for (kk=k-1;kk<=k+1;kk++) {
						if (ii>=0 && ii<xgrid && jj>=0 && jj<ygrid && kk>=0 && kk<=zgrid) {
							llist = grid[ii][jj][kk];
							while (llist) {
							  if (llist->atom->name[0]=='N' && llist->atom->name[1]==' ' && (abs(llist->atom->res->locnum-n_atom1->res->locnum)>2))  {
							    tmp_atom = llist->atom;
							    if (tmp_atom->res!=c_atom2->res) {
  							    dx = o_atom1->x-tmp_atom->x;
  							    dy = o_atom1->y-tmp_atom->y;
  							    dz = o_atom1->z-tmp_atom->z;
  							    dist = dx*dx+dy*dy+dz*dz;
  							    if (dist<min_dist2 && dist<25.0) {
  							      n_atom2=tmp_atom;
  							      min_dist2 = dist;
     						    } 
     						  }
							  } 
							  llist = llist->next;
		          }					
						}
					}
				}
			}
		}

    if (n_atom2) {
      c_atom2 = o_atom2 = NULL;
      atom = n_atom2->res->atoms;
    	while (atom) {
  			if (atom->name[0]=='C' && atom->name[1]==' ') { c_atom2 = atom; }
  			if (atom->name[0]=='O' && atom->name[1]==' ') { c_atom2 = atom; }
  			atom = atom->next;
  		}                      
      
      if (c_atom2) {    
    		hx1 = o_atom1->x-c_atom1->x;
    		hy1 = o_atom1->y-c_atom1->y;
    		hz1 = o_atom1->z-c_atom1->z;
    		dd = -1.081f/sqrt(hx1*hx1+hy1*hy1+hz1*hz1);
    		hx1 *= dd;
    		hy1 *= dd;
    		hz1 *= dd;
    		
    		hx1 += n_atom1->x;
    		hy1 += n_atom1->y;
    		hz1 += n_atom1->z;

    }
*******/


  return ene;
}

// rotates a point around a vector
void rot_point_vector(real *x, real *y, real *z, real u, real v, real w, real angle)
{
  real ux, uy, uz, vx, vy, vz, wx, wy, wz, sa, ca;
  
    sa = sinf(10.0*M_PI*angle/180.0);
    ca = cosf(10.0*M_PI*angle/180.0);
    
    ux = u**x;
    uy = u**y;
    uz = u**z;
    vx = v**x;
    vy = v**y;
    vz = v**z;
    wx = w**x;
    wy = w**y;
    wz = w**z;

    *x = u*(ux+vy+wz)+(*x*(v*v+w*w)-u*(vy+wz))*ca+(-wy+vz)*sa;
    *y = v*(ux+vy+wz)+(*y*(u*u+w*w)-v*(ux+wz))*ca+( wx-uz)*sa;
    *z = w*(ux+vy+wz)+(*z*(u*u+v*v)-w*(ux+vy))*ca+(-vx+uy)*sa;
}


// rotates a peptide plate

void rot_peptide(res_type *res, real angle)
{
	atom_type *atom, *c_atom, *o_atom, *n_atom, *ca_atom1, *ca_atom2;
  real u, v, w, x, y, z, dd;

    if (!res || !res->prev) return;
      
    c_atom = o_atom = n_atom = ca_atom1 = ca_atom2 = NULL;
    
  	atom = res->prev->atoms;
  	while (atom) {
			if (atom->name[0]=='C' && atom->name[1]=='A') ca_atom1 = atom;
			if (atom->name[0]=='C' && atom->name[1]==' ') c_atom = atom;
			if (atom->name[0]=='O' && atom->name[1]==' ') o_atom = atom;
			atom = atom->next;
		}
		
    atom = res->atoms;
  	while (atom) {
			if (atom->name[0]=='C' && atom->name[1]=='A') ca_atom2 = atom;
			if (atom->name[0]=='N' && atom->name[1]==' ') n_atom = atom;
			atom = atom->next;
		}
		
    if (c_atom && o_atom && n_atom && ca_atom1 && ca_atom2) {
      u = ca_atom2->x-ca_atom1->x;
      v = ca_atom2->y-ca_atom1->y;
      w = ca_atom2->z-ca_atom1->z;
      dd = 1.0f/sqrt(u*u+v*v+w*w); 
      u*=dd; v*=dd; w*=dd; // normalize ca-ca vector
      x = n_atom->x-ca_atom1->x;
      y = n_atom->y-ca_atom1->y;
      z = n_atom->z-ca_atom1->z;
      rot_point_vector(&x, &y, &z, u, v, w, angle);
      n_atom->x = x+ca_atom1->x;
      n_atom->y = y+ca_atom1->y;
      n_atom->z = z+ca_atom1->z;
      x = c_atom->x-ca_atom1->x;
      y = c_atom->y-ca_atom1->y;
      z = c_atom->z-ca_atom1->z;
      rot_point_vector(&x, &y, &z, u, v, w, angle);
      c_atom->x = x+ca_atom1->x;
      c_atom->y = y+ca_atom1->y;
      c_atom->z = z+ca_atom1->z;
      x = o_atom->x-ca_atom1->x;
      y = o_atom->y-ca_atom1->y;
      z = o_atom->z-ca_atom1->z;
      rot_point_vector(&x, &y, &z, u, v, w, angle);
      o_atom->x = x+ca_atom1->x;
      o_atom->y = y+ca_atom1->y;
      o_atom->z = z+ca_atom1->z;
    }  		
  
}

void optimize_backbone(mol_type *chain)
{
  int xgrid, ygrid, zgrid;
  atom_list ****grid;
  atom_type *atom;
	res_type *res;
  real ene, min_ene, tot1, tot2;
  int i, k, best;
  FILE *out;
   
  	if (_VERBOSE) printf("Optimizing backbone...\n");

		allocate_grid(&grid, &xgrid, &ygrid, &zgrid);

    tot1 = tot2 = 0.0;
    
		res = chain->residua;
		while (res) {
			ene = hb_energy(res, grid, xgrid, ygrid, zgrid);
			if (ene<-0.5) tot1 += ene;
			res = res->next;
		}

		res = chain->residua;
		while (res) {
		  if (res->type!=7) {
  			ene = hb_energy(res, grid, xgrid, ygrid, zgrid);
  			if (ene<1.0) { // try to optimize
  			  min_ene = ene;
  			  rot_peptide(res, -1.1);
  			  best = 0;
  			  for (i=-10;i<10;i++) {
  			    rot_peptide(res, 0.1);
      			ene = hb_energy(res, grid, xgrid, ygrid, zgrid);
  			    if (ene<min_ene) {
  			      best = i;
  			      min_ene = ene;
  			    }			  
  			  }	
  			  rot_peptide(res,-0.9);		
    			ene = hb_energy(res, grid, xgrid, ygrid, zgrid);
  			  if (min_ene<ene) {
  			    rot_peptide(res,0.1*best);
      			ene = hb_energy(res, grid, xgrid, ygrid, zgrid);
  			  } 
  			}			
  		}
			res = res->next;
		}

		res = chain->residua;
		while (res) {
			ene = hb_energy(res, grid, xgrid, ygrid, zgrid);
			if (ene<-0.5) tot2 += ene;
			res = res->next;
		}

    if (_VERBOSE) printf("Backbone HB energy: before %g, after: %g, difference: %g\n", tot1, tot2, tot2-tot1);

}

int main(int argc, char **argv)
{
  int i, j, next;
  char buf[100];
  char *name=NULL, *ini_name=NULL;
  char *ptr, out_name[1000];
  real f;
  mol_type *mol;
  struct timeb time0, time1;

    for (i=1; i<argc; i++) {
      if (argv[i][0]=='-') {
        next=0;
        for (j=1; j<(int)strlen(argv[i]); j++) {
          switch(argv[i][j]) {
            case 'v': _VERBOSE=1; break;
            case 'c': _CA_OPTIMIZE=0; break;
            case 'e': _BB_REARRANGE=1; break;
            case 'r': _CA_RANDOM=1; break;
            case 'z': _CHIRAL=0; break;
            case 't': _CA_TRAJECTORY=1; break;
            case 'n': _CENTER_CHAIN=1; break;
            case 'b': _REBUILD_BB=0; break;
            case 's': _REBUILD_SC=0; break;
            case 'i': ini_name = argv[++i]; next=1; break;
            case 'g': _PDB_SG=1; break;
            case 'x': _TIME_SEED=1; break;
            case 'o': _XVOLUME=0; break;
            case 'h': _REBUILD_H=0; break;
            case 'q': _BB_OPTIMIZE=1; break;
            case 'p': _CISPRO=1; break;
            case 'u':
              if (sscanf(argv[++i],"%lf",&f)==1) {
                _CA_START_DIST = f;
              }
              next=1;
            break;
            default: {
              printf("Unknown option: %c\n", argv[i][j]);
              return -1;
            }
          }
          if (next) break;
        }
      } else {
        if (!name) name=argv[i];
      }
    }

    if (!name) {
      printf("PULCHRA Protein Chain Restoration Algorithm version %4.2f\n", PULCHRA_VERSION);
      printf("Usage: %s [options] <pdb_file>\n", argv[0]);
      printf("The program default input is a PDB file.\n");
      printf("Output file <pdb_file.rebuild.pdb> will be created as a result.\n");
      printf("Valid options are:\n\n");
      printf("  -v : verbose output (default: off)\n");
      printf("  -n : center chain (default: off)\n");
      printf("  -x : time-seed random number generator (default: off)\n");
      printf("  -g : use PDBSG as an input format (CA=C-alpha, SC or CM=side chain c.m.)\n\n");
      printf("  -c : skip C-alpha positions optimization (default: on)\n");
      printf("  -p : detect cis-prolins (default: off)\n");
      printf("  -r : start from a random chain (default: off)\n");
      printf("  -i pdbfile : read the initial C-alpha coordinates from a PDB file\n");
      printf("  -t : save chain optimization trajectory to file <pdb_file.pdb.trajectory>\n");
      printf("  -u value : maximum shift from the restraint coordinates (default: 0.5A)\n\n");
      printf("  -e : rearrange backbone atoms (C, O are output after side chain) (default: off)\n");

#ifdef COMPILE_BB
      printf("  -b : skip backbone reconstruction (default: on)\n");
      printf("  -q : optimize backbone hydrogen bonds pattern (default: off)\n");
      printf("  -h : outputs hydrogen atoms (default: off)\n");
#endif

#ifdef COMPILE_ROT
      printf("  -s : skip side chains reconstruction (default: on)\n");
      printf("  -o : don't attempt to fix excluded volume conflicts (default: on)\n");
      printf("  -z : don't check amino acid chirality (default: on)\n");
#endif
	  printf("\n");
      return -1;
    }

    for (i=0; i<255; i++) /* prepare hash table*/
      AA_NUMS[i] = 20; /* dummy aa code */
    for (i=0; i<20; i++)
      AA_NUMS[(int)SHORT_AA_NAMES[i]] = i;

    setbuf(stdout,0);

    if (_TIME_SEED) srand(time(NULL)); else srand(1234);

    if (_VERBOSE) printf("PULCHRA Protein Chain Restoration Algorithm version %4.2f\n", PULCHRA_VERSION);

    ftime(&time0);

    chain = new_mol();

    if (read_pdb_file(name,chain,"chain")==FILE_NOT_FOUND) {
      if (_VERBOSE) printf("Can't read the input file!\n");
      return -1;
    }

    if (_VERBOSE) printf("%d residua read.\n", chain->nres);

    chain_length = chain->nres;

    if (_CA_OPTIMIZE) {
      snprintf(out_name,1000,"%s.tra",name);
      ca_optimize(out_name, ini_name);
    }

#ifdef COMPILE_BB
    if (_REBUILD_BB) {
      rebuild_backbone();
      if (_BB_OPTIMIZE) {
        optimize_backbone(chain);
      }
    }
#endif

#ifdef COMPILE_ROT
    if (_REBUILD_BB && _REBUILD_SC) {
      rebuild_sidechains();
      if (_XVOLUME)
        optimize_exvol();
      if (_CHIRAL)
        chirality_check();
    }
#endif

    if (_CENTER_CHAIN) {
      center_chain(chain);
    }


    if (_BB_REARRANGE) {
      if (_VERBOSE) printf("Rearranging backbone atoms...\n");
    }

    ptr = strstr(name,".pdb");
    if (ptr) ptr[0]=0;
      
    snprintf(out_name,1000,"%s.rebuilt.pdb",name);
    
    if (_VERBOSE) printf("Writing output file %s...\n", out_name);
    write_pdb(out_name, chain);

    ftime(&time1);

    if (_VERBOSE) printf("Done. Reconstruction finished in %.3f s.\n", (real)0.001*(1000.*(time1.time-time0.time)+(time1.millitm-time0.millitm)));

  return 0;
}