msspec_python3/msspec/es/es_mod/es_clustering.py

210 lines
8.6 KiB
Python

# coding: utf-8
import unittest
from subprocess import call
import numpy as np
from numpy import sqrt, dot, cross
from numpy.linalg import norm
from ase import Atoms
from ase.io import read, write
from ase.visualize import view
import empty_spheres as esph
import es_tools as tool
# ===================================================================
# List of routines :
"""
=================
Tangent_Fourth_Sphere(Spheres_data, r, inout=1) : From 3 tangent spheres, returns the fourth, tangent to others, with radius r.
inout determines the side of the coordinate.
Spheres_Data_Structure_Extractor (Structure,list) : From Structure, returns data as [[S1][S2]...] where Si = [[xi,yi,zi],ri]
used in Tangent_Fourth routine. List determines the radius we will use.
Spheres_Data_XYZ_Extractor (name,list) : From name, given as "_____.xyz", returns data as [[S1][S2]...] where Si = [[xi,yi,zi],ri]
used in Tangent_Fourth routine. List determines the radius we will use.
Tetrahedron_ES (Spheres_data) : From 4 spheres forming a tetrahedron,returns the sphere tangent to others, with radius r.
Triangle_ES (Spheres_data,R) : Returns the 2 solutions of tangent of 3 spheres with radius R.
=================
"""
# ===================================================================
def Tangent_Fourth_Sphere(Spheres_data, r, inout=1):
# From Spheres_data build like : [[S1],[S2],[S3]] containing spheres informations Si=[[xi,yi,zi],ri], the wanted radius r,
# and with the inout variable (equal to 1 or -1, depends on the facet, to build empty sphere in or out the hull)
# computes the center of the fourth sphere tangent to S1, S2 and S3, and with radius r
# WARNING : Require S1, S2 and S3 are tangent each to another !
# ================
# Read information
S1, S2, S3 = Spheres_data
d1 = S1[1] + r
d2 = S2[1] + r
d3 = S3[1] + r
# Solve the problem in the right space : using u,v,t as base to solution
u = tool.vector_def(S2[0], S1[0])
v = tool.vector_def(S3[0], S1[0])
unorm = tool.vector_norm(u)
vnorm = tool.vector_norm(v)
u = np.multiply(u, 1. / unorm)
v = np.multiply(v, 1. / vnorm)
w = np.multiply(S1[0], -2)
# =====
a = (d2 ** 2 - d1 ** 2 + S1[0][0] ** 2 - S2[0][0] ** 2 + S1[0][1] ** 2 - S2[0][1] ** 2 + S1[0][2] ** 2 -
S2[0][2] ** 2) / (2 * unorm)
b = (d3 ** 2 - d1 ** 2 + S1[0][0] ** 2 - S3[0][0] ** 2 + S1[0][1] ** 2 - S3[0][1] ** 2 + S1[0][2] ** 2 -
S3[0][2] ** 2) / (2 * vnorm)
c = d1 ** 2 - S1[0][0] ** 2 - S1[0][1] ** 2 - S1[0][2] ** 2
# =====
t = np.ndarray.tolist(np.cross(u, v))
tnorm = tool.vector_norm(t)
t = np.multiply(t, 1 / tnorm)
#
alpha = (a - b * np.dot(u, v)) / (1 - np.dot(u, v) ** 2)
beta = (b - a * np.dot(u, v)) / (1 - np.dot(u, v) ** 2)
d = alpha ** 2 + beta ** 2 + 2 * alpha * beta * np.dot(u, v) + alpha * np.dot(u, w) + beta * np.dot(v, w) - c
theta = (-1 * np.dot(w, t) + inout * np.sqrt(np.dot(w, t) ** 2 - 4 * d)) / 2
#
solution = np.ndarray.tolist(np.multiply(u, alpha) + np.multiply(v, beta) + np.multiply(t, theta))
return solution
# ===================================================================
def Spheres_Data_Structure_Extractor(Structure, list):
# From Structure, returns data as [[S1][S2]...] where Si = [[xi,yi,zi],ri]
# used in Tangent_Fourth routine. List determines the radius we will use.
# list determines wich radius we take : 1 for covalent
set_pos = np.ndarray.tolist(Structure.positions)
set_nb = np.ndarray.tolist(Structure.numbers)
data = []
for i in range(0, len(set_nb)):
data.append([set_pos[i], esph.Atom_Radius(Structure, i, list)])
return data
# ===================================================================
def Spheres_Data_XYZ_Extractor(name, list):
# From name, given as "_____.xyz", returns data as [[S1][S2]...] where Si = [[xi,yi,zi],ri]
# used in Tangent_Fourth routine. List determines the radius we will use.
# list determines wich radius we take : 1 for covalent
set = read(name)
set_pos = np.ndarray.tolist(set.positions)
set_nb = np.ndarray.tolist(set.numbers)
data = []
for i in range(0, len(set_nb)):
data.append([set_pos[i], esph.Atom_Radius(set, i, list)])
return data
# ===================================================================
def Tetrahedron_ES(Spheres_data):
# print "Spheres datas :",Spheres_data
S1, S2, S3, S4 = Spheres_data
x1, y1, z1 = S1[0]
r1 = S1[1]
x2, y2, z2 = S2[0]
r2 = S2[1]
x3, y3, z3 = S3[0]
r3 = S3[1]
x4, y4, z4 = S4[0]
r4 = S4[1]
# Solve this problem equals to found V=[x,y,z] and r as : MV = S + r P with :
M = np.array([[x1 - x2, y1 - y2, z1 - z2], [x2 - x3, y2 - y3, z2 - z3], [x3 - x4, y3 - y4, z3 - z4]])
P = np.array([r2 - r1, r3 - r2, r4 - r3])
S = np.array([(r1 ** 2 - r2 ** 2) + (x2 ** 2 - x1 ** 2) + (y2 ** 2 - y1 ** 2) + (z2 ** 2 - z1 ** 2),
(r2 ** 2 - r3 ** 2) + (x3 ** 2 - x2 ** 2) + (y3 ** 2 - y2 ** 2) + (z3 ** 2 - z2 ** 2),
(r3 ** 2 - r4 ** 2) + (x4 ** 2 - x3 ** 2) + (y4 ** 2 - y3 ** 2) + (z4 ** 2 - z3 ** 2)])
S = np.multiply(S, 0.5)
"""
print("M= {}".format(M))
print("P= {}".format(P))
print("S= {}".format(S))
#"""
# MV = S + r P <=> V = Minv S + r MinvP, rewrite as V = Mbar + r Pbar
if np.linalg.det(M) == 0:
print("Singular matrix on the Tetrahedron Fourth SPhere Problem : So we cancel routine")
return 999
Minv = np.linalg.inv(M)
Sbar = np.matmul(Minv, S)
Pbar = np.matmul(Minv, P)
"""
print("Minv= {}\n So verify inversion : Minv * M = \n{}".format(Minv,np.matmul(M,Minv)))
print("Pbar= {}".format(Pbar))
print("Sbar= {}".format(Sbar))
#"""
# V = [x,y,z] depends on r : We need to solve r first : as a root of a 2 degree polygon
Vi = np.array(S1[0])
ri = np.array(S1[1])
D = Sbar - Vi
a = np.linalg.norm(Pbar) ** 2 - 1
b = 2 * (np.dot(D, P) - ri)
c = np.linalg.norm(Sbar) ** 2 - 2 * np.dot(Vi, Sbar) - ri ** 2
delta = b ** 2 - 4 * a * c # Theorically delta >=0
if delta < 0:
return 999
rsol = (-b - np.sqrt(delta)) / (2 * a) # radius of sphere internally tangent
# print("rsol found : {}".format(r))
# print("Verify solution : ar² + br + c = {}".format(a * rsol**2 + b * rsol + c))
# Now we can compute the solution V = x,y,z
V = -1 * (Sbar + np.multiply(Pbar, rsol))
r = tool.distance(V, Vi) - ri
# print("r that should really be : {}".format(r))
# print("Verify solution : ar² + br + c = {}".format(a * r ** 2 + b * r + c))
return [np.ndarray.tolist(V), r]
# ===================================================================
def Triangle_ES(Spheres_data, R):
# Routine posted by Andrew Wagner, Thanks to him.
# Implementaton based on Wikipedia Trilateration article, about intercection of 3 spheres
# This problem solves sphere tangent to 3 spheres : and returns the 2 solutions
P1 = np.array(Spheres_data[0][0])
r1 = Spheres_data[0][1] + R
P2 = np.array(Spheres_data[1][0])
r2 = Spheres_data[1][1] + R
P3 = np.array(Spheres_data[2][0])
r3 = Spheres_data[2][1] + R
temp1 = P2 - P1
if norm(temp1) == 0:
return 666, 666
e_x = temp1 / norm(temp1)
temp2 = P3 - P1
i = dot(e_x, temp2)
temp3 = temp2 - i * e_x
if norm(temp3) == 0:
return 666, 666
e_y = temp3 / norm(temp3)
e_z = cross(e_x, e_y)
d = norm(P2 - P1)
j = dot(e_y, temp2)
x = (r1 * r1 - r2 * r2 + d * d) / (2 * d)
y = (r1 * r1 - r3 * r3 - 2 * i * x + i * i + j * j) / (2 * j)
temp4 = r1 * r1 - x * x - y * y
if temp4 < 0:
# print("The three spheres do not intersect!")
return 666, 666
else:
z = sqrt(temp4)
p_12_a = P1 + x * e_x + y * e_y + z * e_z
p_12_b = P1 + x * e_x + y * e_y - z * e_z
p_12_a = np.ndarray.tolist(p_12_a)
p_12_b = np.ndarray.tolist(p_12_b)
return p_12_a, p_12_b
# ===================================================================