agd.Eikonal.HFM_CUDA.ProjectionTTI

This module attempts to find the parameters of a TTI norm, given a hooke tensor. This boils down to minimizing a polynomial over a sphere. We follow a naive approach, where Newton methods are started from a large number of points in the sphere, and the best result is selected. In principle however, SDP relaxation techniques are applicable.

  1# Copyright 2021 Jean-Marie Mirebeau, University Paris-Sud, CNRS, University Paris-Saclay
  2# Distributed WITHOUT ANY WARRANTY. Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0
  3
  4"""
  5This module attempts to find the parameters of a TTI norm, given a hooke tensor.
  6This boils down to minimizing a polynomial over a sphere. We follow a naive approach, where
  7Newton methods are started from a large number of points in the sphere, and the best result 
  8is selected. In principle however, SDP relaxation techniques are applicable.
  9"""
 10
 11
 12import os
 13import numpy as np
 14from ...Metrics import misc
 15from ... import Sphere
 16from ... import LinearParallel
 17from ...Metrics import Seismic 
 18from ... import AutomaticDifferentiation as ad
 19
 20def rotation_xy(α,β):
 21	"""
 22	A composition of two rotations, along the x and y axes, with the given angles.
 23	"""
 24	,,,,z = np.cos(α),np.sin(α),np.cos(β),np.sin(β),np.zeros_like(α)
 25	return ad.array([
 26		[,z,],
 27		[*,,-*],
 28		[-*,,*]])
 29
 30def rotation_TTI(x,vdim=None):
 31	"""Construction of the rotation associated to the parameter x returned by ProjectionTTI"""
 32	xdim = 1 if vdim==2 else len(x)
 33	if xdim==1: r = LinearParallel.rotation(x)
 34	elif xdim==2: r = rotation_xy(*x)
 35	elif xdim==3: r = Sphere.rotation3_from_ball3(x)[0]
 36	return np.moveaxis(r,0,1) # Reverse the rotation, to match the notebook convention
 37
 38
 39def ProjectionTTI(hooke,ret_rot=True,n_newton=10,samples=None,quaternion=False):
 40	"""
 41	Attempts to produce a TTI norm matching the given Hooke tensors.
 42	Inputs : 
 43	- n_newton = number of Newton steps in the optimization.
 44	- samples = seed points in unit sphere (or number of them)
 45	
 46	Output : 
 47	Returns the score (squared projection error), an HexagonalMaterial, and 
 48	if retx==False : a rotation matrix
 49	if retx==True  : the parameters of the optimal rotation
 50	"""
 51	import cupy as cp
 52	from . import cupy_module_helper
 53	# Check and prepare the Hooke tensors
 54	hdim = len(hooke)
 55	assert hooke.shape[1]==hdim 
 56	assert hdim in (3,6)
 57	vdim = {3:2,6:3}[hdim]
 58	shape = hooke.shape[2:]
 59	n_hooke = np.prod(shape,dtype=int)
 60
 61	float_t = np.float32
 62	int_t = np.int32
 63#	if retx==False: hooke_in = hooke
 64	hooke = misc.flatten_symmetric_matrix(hooke.reshape((hdim,hdim,-1)))
 65	hooke = cp.asarray(np.moveaxis(hooke,0,-1).astype(float_t))
 66
 67	# Prepare the samples, used to initialize the Newton method, which finds the frame
 68	xdim = 1 if vdim==2 else (3 if quaternion else 2)
 69	if samples is None: samples = {1:40,2:400,3:400}[xdim] # default number of samples
 70	if np.ndim(samples)==0:
 71		if xdim==1: # Sample the rotation angle
 72			nX = samples
 73			samples = np.linspace(0,np.pi/2,nX,endpoint=False)[np.newaxis] 
 74		elif xdim==2: # Sample two rotation angles, along the x and y axes 
 75			nX = int(np.round(np.sqrt(samples))) # recall : vti is z-invariant
 76			aX = np.linspace(-np.pi/2,np.pi/2,nX,endpoint=False)
 77			samples = np.array(np.meshgrid(aX,aX,indexing='ij')).reshape(2,-1)
 78		elif xdim==3: # Sample a point in the three dimensional sphere
 79			nX = int(np.round((2*samples)**(1/3))) # Turned quaternion, then rotation
 80			aX,dx = np.linspace(-1,1,nX,retstep=True,endpoint=False)
 81			samples = np.array(np.meshgrid(*(dx/2+aX,)*xdim,indexing='ij'))
 82			inside = (samples**2).sum(axis=0)<=1
 83			samples = samples[:,inside]
 84	assert len(samples)==xdim
 85	samples = samples.reshape(xdim,-1).astype(float_t).T
 86	n_samples = len(samples)
 87	samples = cp.asarray(samples,dtype=float_t)
 88
 89	# Setup the cuda kernel
 90	traits = {'Scalar':float_t, 'xdim_macro':xdim } 
 91
 92	cuda_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"cuda")
 93	date_modified = cupy_module_helper.getmtime_max(cuda_path)
 94	source = cupy_module_helper.traits_header(traits)
 95
 96	source += ['#include "Kernel_ProjectionTTI.h"',
 97	f"// Date cuda code last modified : {date_modified}"]
 98	cuoptions = ("-default-device", f"-I {cuda_path}") 
 99	source = "\n".join(source)
100	module = cupy_module_helper.GetModule(source,cuoptions)
101
102	def SetCst(name,var,dtype):cupy_module_helper.SetModuleConstant(module,name,var,dtype)
103	SetCst('n_newton',n_newton,int_t)
104	SetCst('n_hooke',n_hooke,int_t)
105	SetCst('n_samples',n_samples,int_t)
106
107	ProjTTI = module.get_function('ProjectionTTI')
108
109	# Prepare arguments
110	score = cp.full((n_hooke,),np.nan,dtype=float_t)
111	x_out = cp.full((n_hooke,xdim),np.nan,dtype=float_t)
112	hexa = cp.full((n_hooke,5),np.nan,dtype=float_t)
113
114	hooke,score,x_out,samples,hexa = [cp.ascontiguousarray(e) 
115		for e in (hooke,score,x_out,samples,hexa)]
116	size_i = 64
117	size_o = int(np.ceil(n_hooke/size_i))
118	ProjTTI( (size_o,),(size_i,), (hooke,score,x_out,samples,hexa))
119	del hooke
120
121	# Post processing
122	score.reshape(shape)
123	x_out = x_out.T.reshape((xdim,*shape))
124	hexa = hexa.T.reshape((5,*shape))
125	if xdim==1: x_out = x_out[0]; score*=2
126	return score,hexa,(rotation_TTI(x_out,vdim) if ret_rot else x_out)
def rotation_xy(α, β):
21def rotation_xy(α,β):
22	"""
23	A composition of two rotations, along the x and y axes, with the given angles.
24	"""
25	,,,,z = np.cos(α),np.sin(α),np.cos(β),np.sin(β),np.zeros_like(α)
26	return ad.array([
27		[,z,],
28		[*,,-*],
29		[-*,,*]])

A composition of two rotations, along the x and y axes, with the given angles.

def rotation_TTI(x, vdim=None):
31def rotation_TTI(x,vdim=None):
32	"""Construction of the rotation associated to the parameter x returned by ProjectionTTI"""
33	xdim = 1 if vdim==2 else len(x)
34	if xdim==1: r = LinearParallel.rotation(x)
35	elif xdim==2: r = rotation_xy(*x)
36	elif xdim==3: r = Sphere.rotation3_from_ball3(x)[0]
37	return np.moveaxis(r,0,1) # Reverse the rotation, to match the notebook convention

Construction of the rotation associated to the parameter x returned by ProjectionTTI

def ProjectionTTI(hooke, ret_rot=True, n_newton=10, samples=None, quaternion=False):
 40def ProjectionTTI(hooke,ret_rot=True,n_newton=10,samples=None,quaternion=False):
 41	"""
 42	Attempts to produce a TTI norm matching the given Hooke tensors.
 43	Inputs : 
 44	- n_newton = number of Newton steps in the optimization.
 45	- samples = seed points in unit sphere (or number of them)
 46	
 47	Output : 
 48	Returns the score (squared projection error), an HexagonalMaterial, and 
 49	if retx==False : a rotation matrix
 50	if retx==True  : the parameters of the optimal rotation
 51	"""
 52	import cupy as cp
 53	from . import cupy_module_helper
 54	# Check and prepare the Hooke tensors
 55	hdim = len(hooke)
 56	assert hooke.shape[1]==hdim 
 57	assert hdim in (3,6)
 58	vdim = {3:2,6:3}[hdim]
 59	shape = hooke.shape[2:]
 60	n_hooke = np.prod(shape,dtype=int)
 61
 62	float_t = np.float32
 63	int_t = np.int32
 64#	if retx==False: hooke_in = hooke
 65	hooke = misc.flatten_symmetric_matrix(hooke.reshape((hdim,hdim,-1)))
 66	hooke = cp.asarray(np.moveaxis(hooke,0,-1).astype(float_t))
 67
 68	# Prepare the samples, used to initialize the Newton method, which finds the frame
 69	xdim = 1 if vdim==2 else (3 if quaternion else 2)
 70	if samples is None: samples = {1:40,2:400,3:400}[xdim] # default number of samples
 71	if np.ndim(samples)==0:
 72		if xdim==1: # Sample the rotation angle
 73			nX = samples
 74			samples = np.linspace(0,np.pi/2,nX,endpoint=False)[np.newaxis] 
 75		elif xdim==2: # Sample two rotation angles, along the x and y axes 
 76			nX = int(np.round(np.sqrt(samples))) # recall : vti is z-invariant
 77			aX = np.linspace(-np.pi/2,np.pi/2,nX,endpoint=False)
 78			samples = np.array(np.meshgrid(aX,aX,indexing='ij')).reshape(2,-1)
 79		elif xdim==3: # Sample a point in the three dimensional sphere
 80			nX = int(np.round((2*samples)**(1/3))) # Turned quaternion, then rotation
 81			aX,dx = np.linspace(-1,1,nX,retstep=True,endpoint=False)
 82			samples = np.array(np.meshgrid(*(dx/2+aX,)*xdim,indexing='ij'))
 83			inside = (samples**2).sum(axis=0)<=1
 84			samples = samples[:,inside]
 85	assert len(samples)==xdim
 86	samples = samples.reshape(xdim,-1).astype(float_t).T
 87	n_samples = len(samples)
 88	samples = cp.asarray(samples,dtype=float_t)
 89
 90	# Setup the cuda kernel
 91	traits = {'Scalar':float_t, 'xdim_macro':xdim } 
 92
 93	cuda_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"cuda")
 94	date_modified = cupy_module_helper.getmtime_max(cuda_path)
 95	source = cupy_module_helper.traits_header(traits)
 96
 97	source += ['#include "Kernel_ProjectionTTI.h"',
 98	f"// Date cuda code last modified : {date_modified}"]
 99	cuoptions = ("-default-device", f"-I {cuda_path}") 
100	source = "\n".join(source)
101	module = cupy_module_helper.GetModule(source,cuoptions)
102
103	def SetCst(name,var,dtype):cupy_module_helper.SetModuleConstant(module,name,var,dtype)
104	SetCst('n_newton',n_newton,int_t)
105	SetCst('n_hooke',n_hooke,int_t)
106	SetCst('n_samples',n_samples,int_t)
107
108	ProjTTI = module.get_function('ProjectionTTI')
109
110	# Prepare arguments
111	score = cp.full((n_hooke,),np.nan,dtype=float_t)
112	x_out = cp.full((n_hooke,xdim),np.nan,dtype=float_t)
113	hexa = cp.full((n_hooke,5),np.nan,dtype=float_t)
114
115	hooke,score,x_out,samples,hexa = [cp.ascontiguousarray(e) 
116		for e in (hooke,score,x_out,samples,hexa)]
117	size_i = 64
118	size_o = int(np.ceil(n_hooke/size_i))
119	ProjTTI( (size_o,),(size_i,), (hooke,score,x_out,samples,hexa))
120	del hooke
121
122	# Post processing
123	score.reshape(shape)
124	x_out = x_out.T.reshape((xdim,*shape))
125	hexa = hexa.T.reshape((5,*shape))
126	if xdim==1: x_out = x_out[0]; score*=2
127	return score,hexa,(rotation_TTI(x_out,vdim) if ret_rot else x_out)

Attempts to produce a TTI norm matching the given Hooke tensors. Inputs :

  • n_newton = number of Newton steps in the optimization.
  • samples = seed points in unit sphere (or number of them)

Output : Returns the score (squared projection error), an HexagonalMaterial, and if retx==False : a rotation matrix if retx==True : the parameters of the optimal rotation