import numpy as np
def full_arctangent(x, y):
t = np.arctan2(x, y)
if t < 0:
return t + 2 * np.pi
else:
return t
def cartesian_to_prolate_ellipsoidal(x, y, z, a):
b1 = np.sqrt((x + a) ** 2 + y**2 + z**2)
b2 = np.sqrt((x - a) ** 2 + y**2 + z**2)
sigma = 1 / (2.0 * a) * (b1 + b2)
tau = 1 / (2.0 * a) * (b1 - b2)
phi = full_arctangent(z, y)
mu = np.arccosh(sigma)
nu = np.arccos(tau)
return mu, nu, phi
def prolate_ellipsoidal_to_cartesian(mu, nu, phi, a):
x = a * np.cosh(mu) * np.cos(nu)
y = a * np.sinh(mu) * np.sin(nu) * np.cos(phi)
z = a * np.sinh(mu) * np.sin(nu) * np.sin(phi)
return x, y, z
[docs]
def get_strain_regions(nsectors=(6, 6, 4, 1)):
"""Mark the cells in the mesh.
For instance if you want to mark this mesh accoring to
the AHA 17-segment model, then nsector = [6,6,4,1],
i.e 6 basal, 6 mid, 4 apical and one apex
"""
nlevels = len(nsectors)
pi = np.pi
assert nlevels <= 4
if nlevels == 4:
mus = [90, 60, 30, 10, 0]
elif nlevels == 3:
mus = [90, 60, 30, 0]
elif nlevels == 2:
mus = [90, 45, 0]
else:
mus = [90, 0]
thetas = [np.linspace(pi, 3 * pi, s + 1)[:-1].tolist() + [pi] for s in nsectors]
start = 0
end = nsectors[0]
regions = np.zeros((sum(nsectors), 4))
for i in range(nlevels):
regions.T[0][start:end] = mus[i] * pi / 180
regions.T[3][start:end] = mus[i + 1] * pi / 180
if i != len(nsectors) - 1:
start += nsectors[i]
end += nsectors[i + 1]
start = 0
for j, t in enumerate(thetas):
for i in range(nsectors[j]):
regions.T[1][i + start] = t[i]
regions.T[2][i + start] = t[i + 1]
start += nsectors[j]
return regions
[docs]
def strain_region_number(T, regions):
"""
For a given point in prolate coordinates,
return the region it belongs to.
:param regions: Array of all coordinates for the strain
regions taken from the strain mesh.
:type regions: :py:class:`numpy.array`
:param T: Some value i prolate coordinates
:type T: :py:class:`numpy.array`
Resturn the region number that
T belongs to
"""
"""
The cricumferential direction is a bit
tricky because it goes from -pi to pi.
To overcome this we add pi so that the
direction goes from 0 to 2*pi
"""
lam, mu, theta = T
theta = theta + np.pi
levels = get_level(regions, mu)
if np.shape(regions)[0] + 1 in levels:
return np.shape(regions)[0] + 1
sector = get_sector(regions, theta)
assert len(np.intersect1d(levels, sector)) == 1
return np.intersect1d(levels, sector)[0] + 1
def get_level(regions, mu):
A = np.intersect1d(
np.where((regions.T[3] <= mu))[0],
np.where((mu <= regions.T[0]))[0],
)
if len(A) == 0:
return [np.shape(regions)[0] + 1]
else:
return A
def get_sector(regions, theta):
if not (np.count_nonzero(regions.T[1] <= regions.T[2]) >= 0.5 * np.shape(regions)[0]):
raise ValueError("Surfaces are flipped")
sectors = []
for i, r in enumerate(regions):
if r[1] == r[2]:
sectors.append(i)
else:
if r[1] > r[2]:
if theta > r[1] or r[2] > theta:
sectors.append(i)
else:
if r[1] < theta < r[2]:
sectors.append(i)
return sectors
[docs]
def estimate_focal_point(mesh, axis=0):
r"""Copmute the focal point based on approximating the
endocardial surfaces as a ellipsoidal cap.
.. math::
focal = \sqrt{ l^2 - s^2}
Arguments
---------
mesh: `dolfin.mesh`
The mesh
Returns
-------
focal_point: float
The focal point
"""
max_coord = np.max(mesh.coordinates(), axis)
min_coord = np.min(mesh.coordinates(), axis)
axis = np.abs(max_coord - min_coord)
long_axis = np.max(axis)
short_axis = np.min(axis)
focal = np.sqrt(long_axis**2 - short_axis**2)
return focal