tooba_f.py 20.7 KB
Newer Older
Stelios Karozis's avatar
Stelios Karozis committed
1
import os
Stelios Karozis's avatar
Stelios Karozis committed
2 3
import numpy as np
import scipy.optimize
Stelios Karozis's avatar
Stelios Karozis committed
4 5
import pickle as pkl
import json
Stelios Karozis's avatar
Stelios Karozis committed
6
from progress.bar import Bar
7
#from mpl_toolkits.mplot3d import Axes3D
Stelios Karozis's avatar
Stelios Karozis committed
8 9
import matplotlib.pyplot as plt

Stelios Karozis's avatar
Stelios Karozis committed
10 11 12 13 14 15
from pytrr import (
    read_trr_header,
    read_trr_data,
    skip_trr_data,
    )

Stelios Karozis's avatar
Stelios Karozis committed
16
def fitPlaneLTSQ(XYZ):
17
    #Source: https://gist.github.com/RustingSword/e22a11e1d391f2ab1f2c
Stelios Karozis's avatar
Stelios Karozis committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
    (rows, cols) = XYZ.shape
    G = np.ones((rows, 3))
    G[:, 0] = XYZ[:, 0]  #X
    G[:, 1] = XYZ[:, 1]  #Y
    Z = XYZ[:, 2]
    (a, b, c),resid,rank,s = np.linalg.lstsq(G, Z, rcond=None)
    normal = (a, b, -1)
    nn = np.linalg.norm(normal)
    normal = normal / nn
    return (c, normal)

def angle_between2D(p1, p2):
    #arctan2 is anticlockwise
    ang1 = np.arctan2(*p1[::-1])
    ang2 = np.arctan2(*p2[::-1])
    angle=np.rad2deg((ang1 - ang2) % (2 * np.pi)) #degree
    if angle<180:
        return angle
    else:
        return 360 - angle

def angle_between3D(v1, v2):
# v1 is your firsr vector
# v2 is your second vector
    angle = np.arccos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))) #rad
    angle=180*angle/np.pi #degrees
    if angle<180:
        return angle
    else:
        return 360 - angle

Stelios Karozis's avatar
Stelios Karozis committed
49 50 51 52 53 54 55 56 57
def center_gravity(a):
    m=len(a)
    cg = np.sum(a)/m
    return cg

def topickle(fl, sv_name):
    with open(sv_name+'.pkl', 'wb') as handle:
        pkl.dump(fl, handle, protocol=pkl.HIGHEST_PROTOCOL)

Stelios Karozis's avatar
Stelios Karozis committed
58 59 60 61 62 63
def frompickle(fl):
    with open(fl, 'rb') as handle:
        b = pkl.load(handle)
    return b


Stelios Karozis's avatar
Stelios Karozis committed
64 65 66 67
def tojson(fl, sv_name):
    with open(sv_name+'.json', 'w') as file:
        file.write(json.dumps(str(fl)))

68
def plot_surf(data, normal, c, save_name):
Stelios Karozis's avatar
Stelios Karozis committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    #Plot surface
    fig = plt.figure()
    ax = fig.gca(projection='3d')

    # plot fitted plane
    maxx = np.max(data[:,0])
    maxy = np.max(data[:,1])
    minx = np.min(data[:,0])
    miny = np.min(data[:,1])

    point = np.array([0.0, 0.0, c])
    d = -point.dot(normal)

    # plot original points
    ax.scatter(data[:, 0], data[:, 1], data[:, 2])

    # compute needed points for plane plotting
    xx, yy = np.meshgrid([minx, maxx], [miny, maxy])
    z = (-normal[0]*xx - normal[1]*yy - d)*1. / normal[2]

    # plot plane
    ax.plot_surface(xx, yy, z, alpha=0.2)

    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
95 96
    plt.savefig(save_name+'.png')
    #plt.show()
Stelios Karozis's avatar
Stelios Karozis committed
97 98 99
    plt.clf()
    plt.cla()
    plt.close()
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120

def points2vector(data):
    """
    Defines the line whose direction vector is the eigenvector 
    of the covariance matrix corresponding to the largest eigenvalue, 
    that passes through the mean of the data. 

    parameters:     data = array[[x y z]]

    output:         vector = [X,Y,Z]
    """
    # Calculate the mean of the points, i.e. the 'center' of the cloud
    datamean = data.mean(axis=0)

    # Do an SVD on the mean-centered data.
    uu, dd, vv = np.linalg.svd(data - datamean)

    # Now vv[0] contains the first principal component, i.e. the direction
    # vector of the 'best fit' line in the least squares sense.

    return vv[0]
Stelios Karozis's avatar
Stelios Karozis committed
121 122

def count_frames(trajfile='traj.trr'):
123 124 125 126 127
    """
    Count total frames of .trr file

    parameters:     trajfile = [.trr]
    """
Stelios Karozis's avatar
Stelios Karozis committed
128 129 130 131 132 133 134 135 136 137 138 139
    cnt_fr=0
    with open(trajfile, 'rb') as inputfile:
        for i in range(1000):
            try: 
                header = read_trr_header(inputfile)
                #print('Step: {step}, time: {time}'.format(**header))
                skip_trr_data(inputfile, header)
                cnt_fr=cnt_fr+1
            except EOFError:
                pass
    return cnt_fr

Stelios Karozis's avatar
Stelios Karozis committed
140
def fr_export(trajfile='traj.trr', num_frames=1):
141 142 143 144 145 146 147
    """
    Export frames from gromacs .trr file. 

    parameters:     trajfile = [.trr]
                    num_frames = [number of frames to keep
                                  counting from the end of file]
    """
Stelios Karozis's avatar
Stelios Karozis committed
148 149 150
    print(' ')
    print('Read gmx files progress:')
    print('==============================')
Stelios Karozis's avatar
Stelios Karozis committed
151
    cnt_fr=count_frames(trajfile)
152
    data_all={}
Stelios Karozis's avatar
Stelios Karozis committed
153
    with open(trajfile, 'rb') as inputfile:
Stelios Karozis's avatar
Stelios Karozis committed
154
        bar = Bar('Skipping frames from .trr', max=cnt_fr-num_frames)
Stelios Karozis's avatar
Stelios Karozis committed
155
        for i in range(cnt_fr-num_frames):
Stelios Karozis's avatar
Stelios Karozis committed
156 157 158
            header = read_trr_header(inputfile)
            #print('Step: {step}, time: {time}'.format(**header))
            skip_trr_data(inputfile, header)
Stelios Karozis's avatar
Stelios Karozis committed
159 160 161
            bar.next()
        bar.finish()
        bar = Bar('Read frames from .trr', max=num_frames)
162 163
        for i in range(cnt_fr-num_frames,cnt_fr):
            header = read_trr_header(inputfile)
164
            #print('Step: {step}, time: {time}'.format(**header))
165 166 167
            data = read_trr_data(inputfile, header)
            step='{step}'.format(**header)
            data_all[step]=data
Stelios Karozis's avatar
Stelios Karozis committed
168 169
            bar.next()
        bar.finish()
170
        return data_all
Stelios Karozis's avatar
Stelios Karozis committed
171

Stelios Karozis's avatar
Stelios Karozis committed
172
def read_gro(gro):
173 174 175 176 177 178 179 180 181 182 183 184 185
    """
    Read .gro file and exports 
    1. system name
    2. data number
    3. box size
    4. residue number
    5. residue type
    6. atom type
    7. atom number
    8. free format data (x,y,z,v,u,w)

    parameters:     gro = [.gro]
    """
Stelios Karozis's avatar
Stelios Karozis committed
186 187
    cnt=0
    data_num=0
Stelios Karozis's avatar
Stelios Karozis committed
188 189 190 191 192
    res_num = [] 
    res_type = []  
    atom_type = [] 
    atom_num  = []
    rest_dt = []
193
    cnt_atoms=0
Stelios Karozis's avatar
Stelios Karozis committed
194
    num_lines = sum(1 for line in open(gro))
Stelios Karozis's avatar
Stelios Karozis committed
195
    with open(gro, 'r') as F:
Stelios Karozis's avatar
Stelios Karozis committed
196
        bar = Bar('Read .gro', max=num_lines)
Stelios Karozis's avatar
Stelios Karozis committed
197 198
        for line in F:
            cnt=cnt+1
199 200
            #print(cnt)
            if cnt>2 and cnt<=data_num+2:
Stelios Karozis's avatar
Stelios Karozis committed
201 202
                res_num.append(line[:5])  
                res_type.append(line[5:10])  
203 204 205 206 207 208
                atom_type.append(line[10:15])
                #Compensate for large .gro files
                #ToDo check if data index is the same in the function that follows
                #atom_num.append(line[15:20])
                cnt_atoms=cnt_atoms+1
                atom_num.append(str(cnt_atoms))
Stelios Karozis's avatar
Stelios Karozis committed
209
                rest_dt.append(line[20:])
Stelios Karozis's avatar
Stelios Karozis committed
210 211 212 213
            elif cnt==1:
                system=line[:10]
            elif cnt==2:
                data_num=int(line[:7])
214
            elif cnt>data_num+2:
Stelios Karozis's avatar
Stelios Karozis committed
215
                box_size=line[:50]
Stelios Karozis's avatar
Stelios Karozis committed
216 217
            bar.next()
        bar.finish()
Stelios Karozis's avatar
Stelios Karozis committed
218
        #print(system,data_num,box_size)
Stelios Karozis's avatar
Stelios Karozis committed
219
    return system,data_num,box_size,res_num,res_type,atom_type,atom_num,rest_dt
220 221 222

def domain_decomposition(data,dx,dy,dz):
    """
223 224 225 226 227 228 229 230 231 232 233
    Identify subdomain for each frame of 
    of .trr frame

    parameters:     data = {dictionary input from 'def fr_export'}
                    dx = desired size X of subdomain {units of input}
                    dy = desired size Y of subdomain {units of input}
                    dz = desired size Z of subdomain {units of input}
    
    output:         dictionary of x,y,z grid points per step (frame)
    """
    box_p={}
Stelios Karozis's avatar
Stelios Karozis committed
234 235 236
    print(' ')
    print('Domain decomposition progress:')
    print('==============================')
237
    for step in data.keys():
Stelios Karozis's avatar
Stelios Karozis committed
238
        print('Step: '+step)
239 240 241 242 243 244 245 246
        box_p[step]={}
        xs=int(round(data[step]['box'][0][0]+0.1)) #to round always to bigger number
        ys=int(round(data[step]['box'][1][1]+0.1))
        zs=int(round(data[step]['box'][2][2]+0.1))
        box_x=int(xs/dx)
        box_y=int(ys/dy)
        box_z=int(zs/dz)

Stelios Karozis's avatar
Stelios Karozis committed
247

248
        xx=[]
Stelios Karozis's avatar
Stelios Karozis committed
249
        bar = Bar('X-axis', max=box_x)
Stelios Karozis's avatar
Stelios Karozis committed
250 251
#        for i in range(0,xs+1,dx):
        for i in range(0,box_x):
252
            xx.append(i)
Stelios Karozis's avatar
Stelios Karozis committed
253
            bar.next()
254
        box_p[step]['x']=xx
Stelios Karozis's avatar
Stelios Karozis committed
255
        bar.finish()
256 257

        yy=[]
Stelios Karozis's avatar
Stelios Karozis committed
258
        bar = Bar('Y-axis', max=box_y)
Stelios Karozis's avatar
Stelios Karozis committed
259 260
#        for i in range(0,ys+1,dy):
        for i in range(0,box_y):
261
            yy.append(i)
Stelios Karozis's avatar
Stelios Karozis committed
262
            bar.next()
263
        box_p[step]['y']=yy
Stelios Karozis's avatar
Stelios Karozis committed
264
        bar.finish()
265 266

        zz=[]
Stelios Karozis's avatar
Stelios Karozis committed
267
        bar = Bar('Z-axis', max=box_z)
Stelios Karozis's avatar
Stelios Karozis committed
268 269
#        for i in range(0,zs+1,dz):
        for i in range(0,box_z):
270
            zz.append(i)
Stelios Karozis's avatar
Stelios Karozis committed
271
            bar.next()
272
        box_p[step]['z']=zz
Stelios Karozis's avatar
Stelios Karozis committed
273
        bar.finish()
274 275 276


        xyz=[]
Stelios Karozis's avatar
Stelios Karozis committed
277
        bar = Bar('XYZ-axis', max=box_x*box_y*box_z)
Stelios Karozis's avatar
Stelios Karozis committed
278 279 280 281 282 283
#        for ix in range(0,xs+1,dx):
        for ix in range(0,box_x):
#          for iy in range(0,ys+1,dy):
          for iy in range(0,box_y):
#            for iz in range(0,zs+1,dz):
            for iz in range(0,box_z):
284
                xyz.append([ix,iy,iz])
Stelios Karozis's avatar
Stelios Karozis committed
285 286
                bar.next()
        bar.finish()
287 288 289 290
        box_p[step]['xyz']=xyz

    return box_p

Stelios Karozis's avatar
Stelios Karozis committed
291
def atomid_data(res_num, res_type, atom_type, atom_num, group={}):
292 293 294 295
    """
    Finds the index in list that 'def read_gro' returns,
    and correspond to the atom types in group list

296 297 298 299
    parameters:     res_num=[]
                    atom_type=[]
                    atom_num=[]
                    group={}
300

301
    output:         dictionary {resid:{res_type:{atom_type:[atom_num]}}}
302
    """
Stelios Karozis's avatar
Stelios Karozis committed
303 304 305 306 307
    print(' ')
    print('Create index  progress:')
    print('======================')
    #bar = Bar('Create index step', max=len(group.keys()))
    res_ndx={}
308
    for res,atom in group.items():
Stelios Karozis's avatar
Stelios Karozis committed
309
        res_ndx[res]=[]
310
        for element in atom:
Stelios Karozis's avatar
Stelios Karozis committed
311 312 313 314 315 316 317 318 319
            tmp=[res_num[i].strip()  for i, e in enumerate(atom_type) if e.strip() == element.strip()]
        res_ndx[res].append(tmp)
        #bar.next()
    #bar.finish()

    res_ndx_flat={}
    for res in res_ndx.keys():
        res_ndx_flat[res] = [y for x in res_ndx[res] for y in x]

320
    ndx={}
Stelios Karozis's avatar
Stelios Karozis committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
    for rs in res_ndx_flat.keys():
        bar = Bar(rs, max=len(res_ndx_flat[rs]))
        for resid in res_ndx_flat[rs]:
            ndx[resid.strip()]={}
            for res,atom in group.items():
                ndx[resid][res]={}
                for element in atom:
                    tmp=[atom_num[i].strip()  for i, e in enumerate(atom_type) if e.strip() == element.strip() and resid.strip()==res_num[i].strip() and res.strip()==res_type[i].strip()]
                    if not tmp:
                        ndx[resid].pop(res,None)
                        pass
                    else:
                        ndx[resid][res][element]=tmp
            bar.next()
        bar.finish()
336
    return ndx
337

338
def atom2grid(data, box_p, ndx):
339
    """
340
    Assign atom number that corresponds to ndx (see 'def atomid_data')
341 342 343 344 345
    to sudomains created from 'def domain_decomposition'. The output
    is a the box location (non zero based) in xyz-space for each atom number.

    parameters:     data = {dictionary input from 'def fr_export'}
                    box_p = {dictionary input from 'def domain_decomposition'}
346
                    ndx = {dictionary input from 'def atomid_data'}
347

348 349 350
    output:         dictionaries 
                        1. box_res = {step:{resid:{res_type:{atom_type:{atom_num:(subX,subYsubZ)}}}}}
                        2. box_res_rev = {step:{resid:{res:{(subX,subYsubZ):[atom_num]}}}}
351
    """
Stelios Karozis's avatar
Stelios Karozis committed
352 353 354
    print(' ')
    print('Assing atom to grid progress:')
    print('==============================')
355 356

    box_res={}
357
    box_res_rev = {}
358
    for step in data.keys():
Stelios Karozis's avatar
Stelios Karozis committed
359 360
        #print('Step: '+step)
        bar = Bar('Step: '+step, max=len(ndx.keys()))
361
        box_res[step]={}
362
        box_res_rev[step] = {}
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
        for resid in ndx.keys():
            box_res[step][resid]={}
            box_res_rev[step][resid] = {}
            for res in ndx[resid].keys():
                box_res[step][resid][res]={}
                box_res_rev[step][resid][res]={}
                for atom in ndx[resid][res].keys():
                    box_res[step][resid][res][atom]={}
                    box_res_rev[step][resid][res][atom] = {}
                    for atomnum in ndx[resid][res][atom]:
                        #data[step]['x'][atom_num-1][x(0),y(1),z(2)]

                        xx=data[step]['x'][int(atomnum)-1][0]
                        cnt_x=-1
                        prev=-1
                        for ix in box_p[step]['x']:
                            cnt_x=cnt_x+1
                            if xx<ix and xx>prev:
                                prev=ix
                                break

                        yy=data[step]['x'][int(atomnum)-1][1]
                        cnt_y=-1
                        prev=-1
                        for iy in box_p[step]['y']:
                            cnt_y=cnt_y+1
                            if yy<iy and yy>prev:
                                prev=iy
                                break

                        zz=data[step]['x'][int(atomnum)-1][2]
                        cnt_z=-1
                        prev=-1
                        for iz in box_p[step]['z']:
                            cnt_z=cnt_z+1
                            if zz<iz and zz>prev:
                                prev=iz
                                break

                        box_res[step][resid][res][atom][atomnum]=(cnt_x,cnt_y,cnt_z)

                    #Make subdomain position the key and group the residues
                    for key, value in sorted(box_res[step][resid][res][atom].items()):
                        box_res_rev[step][resid][res].setdefault(value, []).append(key.strip())
                    #Remove  atom_type as key of dictionary
                    box_res_rev[step][resid][res].pop(atom,None)
                    #If atoms of residue lie in more than one subdomain, put the all in the first one
                    if len(box_res_rev[step][resid][res]) > 1:
                        tmp=[]
                        flattened_list=[]
                        kk=[]
                        for sb in box_res_rev[step][resid][res].keys():
                            kk.append(sb)
                            tmp.append(box_res_rev[step][resid][res][sb])
                        #Remove keys
                        for k in kk: 
                            box_res_rev[step][resid][res].pop(k,None)
                        #flatten the lists
                        flattened_list = [y for x in tmp for y in x]
                        #Keep first key and results
                        box_res_rev[step][resid][res][kk[0]]=flattened_list
                    #box_res_rev[step][resid][res]=[i  for i, e in enumerate(box_res_rev[step][resid][res]) if e.strip() != .strip()]
Stelios Karozis's avatar
Stelios Karozis committed
425 426
            bar.next()
        bar.finish()
427 428 429 430 431 432 433 434 435
    return box_res, box_res_rev

def sub_coord(box, data, res_num=[]):
    """
    Use the box_res_rev from 'def atom2grid' and data from 'def fr_export'
    and groups all the XYZ coordinates per subdomain

    parameters:         box = {step:{resid:{res:{(subX,subYsubZ):[atom_num]}}}}
                        data = {step:{obj:[x,y,z]}}
436
    
437 438 439
    output:             norm = {step:{subdomain:[[X1,Y1,Z1] ... [Xn,Yn,Zn]]}}
                        vector = {step:{subdomain:resid:{[[X1,Y1,Z1] ... [Xn,Yn,Zn]]}}}
    """
Stelios Karozis's avatar
Stelios Karozis committed
440 441 442 443
    print(' ')
    print('Groupby subdomain progress:')
    print('==============================')

444 445 446
    coord_norm={}
    coord_vector={}
    for step in box.keys():
Stelios Karozis's avatar
Stelios Karozis committed
447

448 449
        coord_norm[step]={}
        coord_vector[step]={}
Stelios Karozis's avatar
Stelios Karozis committed
450 451 452 453 454 455 456 457
        for resid in box[step].keys():
                for res in box[step][resid].keys():
                    for subdomain in box[step][resid][res].keys():
                        coord_norm[step][subdomain]=[]
                        coord_vector[step][subdomain]={}

        rs=[]
        bar = Bar('Step: '+step, max=len(box[step].keys()))
458 459 460
        for resid in box[step].keys():
            for res in box[step][resid].keys():
                for subdomain in box[step][resid][res].keys():
Stelios Karozis's avatar
Stelios Karozis committed
461 462 463 464
                    #tmp=[]
                    if (resid,subdomain) not in rs:
                        rs.append((resid,subdomain))
                        coord_vector[step][subdomain][resid]=[]
465 466
                    for atomnum in box[step][resid][res][subdomain]:
                        #tmp_atom=[]
Stelios Karozis's avatar
Stelios Karozis committed
467
                        #tmp.append(data[step]['x'][int(atomnum)-1])
468 469
                        #tmp_atom.append(list(data[step]['x'][int(atomnum)-1])) #not append to previous
                        coord_norm[step][subdomain].append(list(data[step]['x'][int(atomnum)-1]))
Stelios Karozis's avatar
Stelios Karozis committed
470 471 472
                        coord_vector[step][subdomain][resid].append(list(data[step]['x'][int(atomnum)-1]))
            bar.next()
        bar.finish()
473 474 475 476 477 478 479 480 481 482 483 484 485
    return coord_norm, coord_vector

def coord2norm(coord,img=True):
    """
    Use the coord from 'def sub_coord' and replaces XYZ coordinates
    with c, normal of surface that fits the points. Also it can plot
    the surfaces for each subdomain

    parameters:         coord = {step:{subdomain:[[X1,Y1,Z1] ... [Xn,Yn,Zn]]}}
                        img = True or False
    
    output:             dictionary = {step:{subdomain:{c:int, normal:array[]}}}
    """
Stelios Karozis's avatar
Stelios Karozis committed
486 487 488 489
    print(' ')
    print('Finding surface progress:')
    print('==============================')

490 491 492
    surf={}
    for step in coord.keys():
        surf[step]={}
Stelios Karozis's avatar
Stelios Karozis committed
493
        bar = Bar('Step: '+step, max=len(coord[step].keys()))
494 495
        for subdomain in coord[step].keys():
            c,normal = fitPlaneLTSQ(np.array(coord[step][subdomain]))
Stelios Karozis's avatar
Stelios Karozis committed
496 497 498 499
            cgx = center_gravity(coord[step][subdomain])
            cgy = center_gravity(coord[step][subdomain])
            cgz = center_gravity(coord[step][subdomain])
            surf[step][subdomain]={'c':c, 'normal':normal, 'cg':[cgx,cgy,cgz]}
500 501 502 503 504 505 506
            #Change save variable if you want to save .png elsewere
            save='png/'+str(subdomain)
            if img==True:
                try:
                    plot_surf(np.array(coord[step][subdomain]), normal, c, save)
                except:
                    print("ERROR: Folder png/ doesn't exist in root")
Stelios Karozis's avatar
Stelios Karozis committed
507 508 509
            bar.next()
        bar.finish()
    return surf
510

Stelios Karozis's avatar
Stelios Karozis committed
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
def coord2norm2cg(coord_vector,img=True):
    """
    Use the coord_vector from 'def sub_coord' and replaces XYZ coordinates
    with c, normal of surface that fits the points and calculates center of gravity
    for each resid. Also it can plot the surfaces for each subdomain

    parameters:         coord = {step:{subdomain:{resid:[[X1,Y1,Z1] ... [Xn,Yn,Zn]]}}
                        img = True or False
    
    output:             dictionary = {step:{subdomain:{c:int, normal:array[], resid:[cgx, cgy, cgz]}}}
    """
    print(' ')
    print('Finding surface and gravity point progress:')
    print('==========================================')

    surf={}
    for step in coord_vector.keys():
        surf[step]={}
        bar = Bar('Step: '+step, max=len(coord_vector[step].keys()))
        for subdomain in coord_vector[step].keys():
            surf[step][subdomain]={}
            #surf_points=[]
            res_cg={}
            cnt=0
            for resid in coord_vector[step][subdomain].keys():
                cnt=cnt+1
                cgx = center_gravity(coord_vector[step][subdomain][resid])
                cgy = center_gravity(coord_vector[step][subdomain][resid])
                cgz = center_gravity(coord_vector[step][subdomain][resid])
                res_cg[resid]=[cgx,cgy,cgz]
                if cnt==1:
                    surf_points=coord_vector[step][subdomain][resid]
                else:
                    surf_points=surf_points+coord_vector[step][subdomain][resid]
            c,normal = fitPlaneLTSQ(np.array(surf_points))
            surf[step][subdomain]={'c':c, 'normal':normal}
            surf[step][subdomain].update(res_cg)
            save='png/'+str(subdomain)
            if img==True:
                try:
                    plot_surf(np.array(surf_points), normal, c, save)
                except:
                    print("ERROR: Folder png/ doesn't exist in root")
            bar.next()
        bar.finish()
556 557 558 559 560 561 562
    return surf

def coord2vector(coord_vector):
    """
    Use the coord_vector from 'def sub_coord' and replaces XYZ coordinates
    with vector of best fit line

Stelios Karozis's avatar
Stelios Karozis committed
563 564 565
    parameters:         coord = {step:{subdomain:{resid:[[X1,Y1,Z1] ... [Xn,Yn,Zn]]}}
        
    output:             dictionary = {step:{subdomain:{resid:[x, y, z]}}
566
    """
Stelios Karozis's avatar
Stelios Karozis committed
567 568 569
    print(' ')
    print('Finding surface progress:')
    print('==============================')
570 571 572
    vector={}
    for step in coord_vector.keys():
        vector[step]={}
Stelios Karozis's avatar
Stelios Karozis committed
573
        bar = Bar('Step: '+step, max=len(coord_vector[step].keys()))
574 575 576 577 578
        for subdomain in coord_vector[step].keys():
            vector[step][subdomain]={}
            for resid in coord_vector[step][subdomain].keys():
                vv = points2vector(np.array(coord_vector[step][subdomain][resid]))
                vector[step][subdomain][resid]=vv
Stelios Karozis's avatar
Stelios Karozis committed
579 580
            bar.next()
        bar.finish()
Stelios Karozis's avatar
Stelios Karozis committed
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
    return vector

def SurfVector_angle(surf,vector):
    """
    Use the surf and vector from 'def coord2norm' and 'def coord2vector'
    and calculate the angle between them for every step, sudomain and resid

    parameters:         surf = {step:{subdomain:{c:int, normal:array[]}}}
                        vector = {step:{subdomain:{resid:[x, y, z]}}
    
    output:             angle = {step:{subdomain:{resid:angle}}
    """
    angle={}
    for step in surf.keys():
        angle[step]={}
        for sudomain in surf[step].keys():
            angle[step][sudomain]={}
            for resid in vector[step][sudomain].keys():
                P1=tuple(surf[step][sudomain]['normal'])
                P2=tuple(vector[step][sudomain][resid])
                #print(tbf.angle_between3D(P1,P2))
                angle[step][sudomain][resid]=angle_between3D(P1,P2)
    return angle