Source code for genepy3d.util.geo

"""Support functions for geometrical calculus.
"""

import numpy as np

[docs] def l2(p1, p2): """L2 distance between two points p1, p2. Args: p1 (ndarray): first point. p2 (ndarray): second point. Returns: a float. """ return np.sqrt(np.sum((p1-p2)**2))
[docs] def norm(p): """Norm of vectors. Args: p (ndarray): a vector or an array of vectors. each vector is at one row of the array, the array columns are the vector positions. Returns: an array of float. Examples: .. code-block:: python import numpy as np from genepy3d.util import geo vecarr = np.array([[1,2,3],[1,2,3]]) # two vectors with similar values geo.norm(vecarr) """ if len(p.shape)==2: # list of points return np.sqrt(np.sum(p**2,axis=1)) elif len(p.shape)==1: # only one point return np.sqrt(np.sum(p**2)) else: raise ValueError('Accept only vector or array of vectors.')
[docs] def vector2points(p1,p2,normalize=True): """Calculate the vector from point p1 to point p2 with/without normalization. Args: p1 (ndarray): first point. p2 (ndarray): second point. normalized (bool): if True, then normalize the vector. Returns: an ndarray. """ v = p2 - p1 if((normalize==True)&(norm(v)!=0)): return v/norm(v) else: return v
[docs] def vector_axes_angles(v): """Calculate the angles between vector v and the three axes x, y and z. Args: v (ndarray): a vector. Returns: tuple of floats of the angles between v and axes x, y and z respectively. """ if norm(v)!=0: vx = np.array([1.,0.,0.]) vy = np.array([0.,1.,0.]) vz = np.array([0.,0.,1.]) thetax = np.arccos(np.dot(v,vx)/(norm(v)*norm(vx))) thetay = np.arccos(np.dot(v,vy)/(norm(v)*norm(vy))) thetaz = np.arccos(np.dot(v,vz)/(norm(v)*norm(vz))) return (thetax, thetay, thetaz) else: return (np.nan,np.nan,np.nan)
[docs] def vector_spherical_angles(v): """Spherical angles of vector. Args: v (ndarray): a vector. Returns: a list [theta, phi] of spherical angles. Notes: The spherical coordinates are specified by - azimuthal angle (theta) between the orthogonal projection of the vector v (on xy plane) and the x-vector. - polar angle (phi) between the vector v and the z-vector. """ orientations=[] if np.abs(v[0])<0.1 : if np.abs(v[1])<0.1 : phi=0 # If phi equals to zero then the component is collinear to Oz axis and # theta has no numerical value theta=None else : theta=np.pi/2 phi=np.arccos(v[2]) else : phi=np.arccos(v[2]) theta=np.arctan(v[1]/v[0]) orientations.append([theta,phi]) return orientations
[docs] def angle2vectors(a,b): """Return angle between two vectors a and b. Args: a (ndarray): a vector. b (ndarray): a vector. Returns: a float. """ if (norm(a) == 0) | (norm(b)==0): return np.nan else: return np.arccos(np.dot(a,b)/(norm(a)*norm(b)))
[docs] def angle3points(a,b,c): """Return angle between three points where b is the center point. It is the angle between two vectors (b->a) and (b->c). Args: a (ndarray): a point. b (ndarray): a point. c (ndarray): a point. Returns: a float. """ ab = b - a bc = c - b if (norm(ab)==0)|(norm(bc)==0): return np.nan else: cosine_angle = np.dot(ab, bc) / (norm(ab) * norm(bc)) return np.arccos(cosine_angle)
[docs] def geo_len(P): """Return geodesic length of a curve defined from an array of points. The first point and the last point are the begin and the end of the curve. Args: P (ndarray): array of points. Returns: a float. Examples: .. code-block:: python import numpy as np from genepy3d.util import geo P = np.array([[1,1,1],[2,2,2],[3,3,3]]) # curve with three points (1,1,1), (2,2,2) and (3,3,3) geo.geo_len(P) """ n = P.shape[0] s = 0 for i in range(n-1): p1, p2 = P[i], P[i+1] s = s + l2(p1,p2) return s
[docs] def active_brownian_2d(n,v=1e-6,omega=0.,p0=[0,0],dt=1e-3,R=1e-6,T=300.,eta=1e-3,seed_point=None): """Generate an active Brownian motion in 2D. We implemented the algorithm from the Volpe et al. paper [1]. Args: n (int): number of positions generated from the simulation. p0 (ndarray): init position. v (float): translation speed. omega (float): rotation speed. dt (float): time step. R (float): particle radius. T (float): environment temperature. eta (float): fluid viscocity. Returns: A tuple containing - P (array of float): list of 2D positions. - t (array of float): corresponding times. References: .. [1] Volpe, G., Gigan, S., & Volpe, G. (2014). Simulation of the active Brownian motion of a microswimmer. American Journal of Physics, 82(7), 659-664. DOI:10.1119/1.4870398. """ if seed_point is not None: np.random.seed(seed_point) kB = 1.38e-23 # Boltzmann constant [J/K] gamma = 6*np.pi*R*eta # friction coefficient [Ns/m] DT = (kB*T)/gamma # translational diffusion coefficient [m^2/s] DR = (6*DT)/(8*R**2) # rotational diffusion coefficient [rad^2/s] P = np.zeros((n,2)) P[0] = p0 # init point theta = 0 # init angle for i in range(n-1): # translational diffusion step P[i+1] = P[i] + np.sqrt(2*DT*dt)*np.random.randn(1,2) # rotational diffusion step theta = theta + np.sqrt(2*DR*dt)*np.random.randn(1,1)[0,0] # torque step theta = theta + dt*omega # drift step P[i+1] = P[i+1] + dt*v*np.array([np.cos(theta), np.sin(theta)]) t = np.arange(0,n*dt,dt) return (P,t)
[docs] def active_brownian_3d(n,v=1e-6,omega=0.,p0=[0,0,0],dt=1e-3,R=1e-6,T=300.,eta=1e-3,seed_point=None): """Generate an active Brownian motion in 3D. We adapted the algorithm of active brownian motion in 2D from the paper of Volpe et al. [1] for 3D case. Args: n (int): number of positions generated from the simulation. p0 (ndarray): init position. v (float): translation speed. omega (float): rotation speed. dt (float): time step. R (float): particle radius. T (float): environment temperature. eta (float): fluid viscocity. Returns: A tuple containing - P (array of float): list of 3D positions. - t (array of float): corresponding times. References: .. [1] Volpe, G., Gigan, S., & Volpe, G. (2014). Simulation of the active Brownian motion of a microswimmer. American Journal of Physics, 82(7), 659-664. DOI:10.1119/1.4870398. """ if seed_point is not None: np.random.seed(seed_point) kB = 1.38e-23 # Boltzmann constant [J/K] DT = (kB*T)/(6*np.pi*eta*R) # translational diffusion coefficient [m^2/s] DR = (kB*T)/(8*np.pi*eta*R**3) # rotational diffusion coefficient [rad^2/s] P = np.zeros((n,3)) P[0] = p0 # init point theta = 0 # init angle phi = 0 # init angle for i in range(n-1): # translational diffusion step P[i+1] = P[i] + np.sqrt(2*DT*dt)*np.random.randn(1,3) # rotational diffusion step theta = theta + np.sqrt(2*DR*dt)*np.random.randn(1,1)[0,0] phi = phi + np.sqrt(2*DR*dt)*np.random.randn(1,1)[0,0] # torque step if omega != 0: theta = theta + dt*np.sin(omega) phi = phi + dt*np.cos(omega) # drift step P[i+1] = P[i+1] + dt*v*np.array([np.cos(theta)*np.sin(phi), np.sin(theta)*np.sin(phi), np.cos(phi)]) t = np.arange(0,n*dt,dt) return (P,t)
[docs] def emd(X,Y,return_flows=False): """Compute the Earth Mover's Distance (EMD) [1] between two point clouds X and Y. We used the emd func from POT library. Detail is here: https://pythonot.github.io/all.html#ot.emd Args: X (ndarray): array of points. Y (ndarray): array of points. return_flows (bool): if True return travelled flows between X and Y. Returns: if return_flows is False, then only the emd distance is returned, otherwise a tuple of distance and array of flows. References: .. [1] https://en.wikipedia.org/wiki/Earth_mover%27s_distance Examples: .. code-block:: python import numpy as np from genepy3d.util import geo X = np.array([[1,1,1],[2,2,2]]) Y = np.array([[3,3,3],[4,4,4]]) loss = geo.emd(X,Y) """ import ot # optimal transport lib n = X.shape[0] M = ot.dist(X, Y) a, b = np.ones((n,)) / n, np.ones((n,)) / n # uniform distribution on samples loss = ot.emd2(a, b, M) if return_flows==True: F = ot.emd(a, b, M) return (loss,F) else: return loss
# def emd(X,Y,return_flows=False): # """Compute Earth Mover's Distance (EMD) between two nD points X and Y. # Args: # X (nD array): nD points. # Y (nD array): nD points. # return_flows (bool): if True return matching flows between X and Y. # Returns: # if return_flows is False, then only distance value else a tuple of distance and array of flows. # """ # from emd import emd as emddev # return emddev(X,Y,return_flows=return_flows)
[docs] def rotation_matrix(u,theta): """Calculate the rotation matrix corresponding to a rotation of angle theta around a vector u in 3D. Args: u (ndarray) : 3D vector for rotation direction. theta (float) : rotation angle in radians. Returns: (ndarray of shape (3,3)) : rotation matrix. Examples: .. code-block:: python import numpy as np from genepy3d.util import geo u = np.array([1.,0.,0.]) # unit x-vector theta = np.pi/2. # rotation of 90 degrees M = geo.rotation_matrix(u,theta) # rotation matrix of 90 degrees around the unit x-vector """ P=np.kron(u,u).reshape(3,3) Q=np.diag([u[1]],k=2)+np.diag([-u[2],-u[0]],k=1)+np.diag([u[2],u[0]],k=-1)+np.diag([-u[1]],k=-2) return P+np.cos(theta)*(np.eye(3)-P)+np.sin(theta)*Q