sandbox/prouvost/AMR_tools/linalg.h
Here you will find some tools to manipulate tensors, and in particular some linear algebra tools (eigen values and vectors of a symmetric matrix).
Pseudo tensor and vector
These structures are similar to basilisk tensors and vectors, but they do not store all the fields, they are only used locally in foreach() loops. Useful to use less memory.
N.B. : these structures are originally defined in log-conform, but with events which occurs every iteration. So include the log-conform library would result in creating and calculating a lot of variables for nothing.
#if dimension==2
typedef struct { double x, y;} pseudo_v;
typedef struct { pseudo_v x, y;} pseudo_t;
#endif
#if dimension==3
typedef struct { double x, y, z;} pseudo_v;
typedef struct { pseudo_v x, y, z;} pseudo_t;
#endif
Function declaration
void symmetrize_tensor (tensor t);
void multiply_tensor (Point point, tensor a, tensor b, tensor result);
void transpose_tensor (Point point, tensor a, tensor t_a);
void matrix_from_tensor (double mat[dimension][dimension], pseudo_t t);
void tensor_from_matrix (pseudo_t t, double mat[dimension][dimension]);
void matrix_from_pseudo_t (double mat[dimension][dimension], pseudo_t t);
pseudo_t pseudo_t_from_matrix (double mat[dimension][dimension]);
void diagonal_tensor_from_vector (Point point, vector v, tensor t);
void trace_tensor (Point point, tensor t, scalar trace);
Eigeenvalues and vectors of symmetric matrix
They are computed in basilisk in src/lambda2.h, in order to compute the lambda2 criterion on vortex.
However, write #include “lambda2.h” does not work. This is due to the function lambda2 defined in the file, which uses the .z component of a vector : that makes that the code can’t compile in 2D due to a function we don’t use… So, for now, the functions eigsrt and eigenvalues are just copied from src/lambda2.h, and put here. (copy 20/01/2020).
begin of copy
static void eigsrt (double d[dimension],
double v[dimension][dimension])
{
int k, j, i;
double p;
for (i = 0; i < dimension - 1; i++) {
p = d[k = i];
for (j = i + 1; j < dimension; j++)
if (d[j] >= p)
p = d[k = j];
if (k != i) {
d[k] = d[i];
d[i] = p;
for (j = 0; j < dimension; j++) {
p = v[j][i];
v[j][i] = v[j][k];
v[j][k] = p;
}
}
}
}
#define ROTATE(a,i,j,k,l) { \
g=a[i][j];h=a[k][l];a[i][j]=g-s*(h+g*tau);a[k][l]=h+s*(g-h*tau);}
eigenvalues: @a: a symmetric matrix. @d: a vector. @v: another matrix. Fills @d (resp. @v) with the eigenvalues (resp. eigenvectors) of matrix @a.
void eigenvalues (double a[dimension][dimension],
double d[dimension],
double v[dimension][dimension])
{
int j, iq, ip, i;
double tresh, theta, tau, t, sm, s, h, g, c, b[dimension], z[dimension];
for (ip = 0; ip < dimension; ip++) {
for (iq = 0; iq < dimension; iq++)
v[ip][iq] = 0.0;
v[ip][ip] = 1.0;
}
for (ip = 0; ip < dimension; ip++) {
b[ip] = d[ip] = a[ip][ip];
z[ip] = 0.0;
}
for (i = 1; i <= 50; i++) {
sm = 0.0;
for (ip = 0; ip < dimension - 1; ip++) {
for (iq = ip + 1; iq < dimension; iq++)
sm += fabs (a[ip][iq]);
}
if (sm == 0.0) {
eigsrt (d, v);
return;
}
if (i < 4)
tresh = 0.2*sm/(dimension*dimension);
else
tresh = 0.0;
for (ip = 0; ip < dimension - 1; ip++) {
for (iq = ip + 1; iq < dimension; iq++) {
g = 100.0*fabs (a[ip][iq]);
if (i > 4 && fabs(d[ip]) + g == fabs(d[ip]) &&
fabs(d[iq]) + g == fabs(d[iq]))
a[ip][iq] = 0.0;
else if (fabs (a[ip][iq]) > tresh) {
h = d[iq] - d[ip];
if (fabs(h) + g == fabs(h))
t = a[ip][iq]/h;
else {
theta = 0.5*h/a[ip][iq];
t = 1.0/(fabs (theta) + sqrt (1.0 + theta*theta));
if (theta < 0.0) t = -t;
}
c = 1.0/sqrt (1 + t*t);
s = t*c;
tau = s/(1.0 + c);
h = t*a[ip][iq];
z[ip] -= h;
z[iq] += h;
d[ip] -= h;
d[iq] += h;
a[ip][iq] = 0.0;
for (j = 0; j <= ip - 1; j++)
ROTATE (a, j, ip, j, iq);
for (j = ip + 1; j <= iq - 1; j++)
ROTATE (a, ip, j, j, iq);
for (j = iq + 1; j < dimension; j++)
ROTATE(a, ip, j, iq, j);
for (j = 0; j < dimension; j++)
ROTATE(v, j, ip, j, iq);
}
}
}
for (ip = 0; ip < dimension; ip++) {
b[ip] += z[ip];
d[ip] = b[ip];
z[ip] = 0.0;
}
}
/* Too many iterations */
for (i = 0; i < dimension; i++) {
for (j = 0; j < dimension; j++)
fprintf (stderr, "%10.3g ", a[i][j]);
fprintf (stderr, "\n");
}
assert (false);
}
End of the copy
Tensor eigen values and vectors
The above function for eigenvalues and vectors is written for matrix and not basilisk tensors.
void tensor_eigen(pseudo_t t, pseudo_v * eig_val, pseudo_t * eig_vec) {
double matrix[dimension][dimension];
double eig_vec_matrix[dimension][dimension];
double eig_val_vector[dimension];
// store tensor in matrix
matrix_from_pseudo_t(matrix, t);
// compute eigenvalues
eigenvalues (matrix, eig_val_vector, eig_vec_matrix);
// store eigen values in basilisk vector
eig_val->x = eig_val_vector[0];
#if dimension > 1
eig_val->y = eig_val_vector[1];
#endif
#if dimension > 2
eig_val->z = eig_val_vector[2];
#endif
// store rotation tensor (i.e. eigen vectors matrix as they are orthonormal)
*eig_vec = pseudo_t_from_matrix(eig_vec_matrix);
}
Other tools
Symmetric local tensor
H_R(u) = \frac{H^*(u) + H^*(u)^t}{2}
N.B. : we use the fact that the diagonal elements are not changing.
It modify the tensor in place.
pseudo_t local_symmetrize_pseudo_t (pseudo_t t) {
pseudo_t tmp;
// same diagonal elements
foreach_dimension()
tmp.x.x = t.x.x;
// mean of non-diagonal elements
#if dimension > 1
tmp.x.y = ( t.x.y + t.y.x ) / 2.;
tmp.y.x = t.x.y;
#if dimension > 2
tmp.x.z = ( t.x.z + t.z.x ) / 2.;
tmp.z.x = t.x.z;
tmp.y.z = ( t.y.z + t.z.y ) / 2.;
tmp.z.y = t.y.z;
#endif
#endif
return tmp;
}
Multiplication of local tensor
Multiplication of a pseudo-tensor by a pseudo-tensor in a pseudo-tensor.
pseudo_t multiply_local_tensor (pseudo_t a, pseudo_t b) {
pseudo_t result;
foreach_dimension() {
#if dimension == 1
result.x.x = a.x.x*b.x.x;
#endif
#if dimension == 2
result.x.x = a.x.x*b.x.x + a.x.y*b.y.x;
result.x.y = a.x.x*b.x.y + a.x.y*b.y.y;
#endif
#if dimension == 3
result.x.x = a.x.x*b.x.x + a.x.y*b.y.x + a.x.z*b.z.x;
result.x.y = a.x.x*b.x.y + a.x.y*b.y.y + a.x.z*b.z.y;
result.x.z = a.x.x*b.x.z + a.x.y*b.y.z + a.x.z*b.z.z;
#endif
}
return result;
}
Transposition of local tensor
Transposition of a pseudo-tensor.
pseudo_t transpose_pseudo_t (pseudo_t t) {
pseudo_t tmp;
foreach_dimension() {
tmp.x.x = t.x.x;
#if dimension > 1
tmp.x.y = t.y.x;
#endif
#if dimension > 2
tmp.x.z = t.z.x;
#endif
}
return tmp;
}
Tensor - matrix - local tensor transformations
Transform basilisk tensor in a pseudo-tensor.
pseudo_t create_local_tensor (Point point, tensor t) {
pseudo_t tmp;
tmp.x.x = t.x.x[];
#if dimension > 1
tmp.y.y = t.y.y[];
tmp.x.y = t.x.y[];
tmp.y.x = t.y.x[];
#endif
#if dimension > 2
tmp.z.z = t.z.z[];
tmp.x.z = t.x.z[];
tmp.y.z = t.y.z[];
tmp.z.x = t.z.x[];
tmp.z.y = t.z.y[];
#endif
return tmp;
}
Transform basilisk tensor in a matrix.
void matrix_from_pseudo_t (double mat[dimension][dimension], pseudo_t t) {
mat[0][0] = t.x.x;
#if dimension > 1
mat[1][1] = t.y.y;
mat[0][1] = t.x.y;
mat[1][0] = t.y.x;
#endif
#if dimension > 2
mat[2][2] = t.z.z;
mat[0][2] = t.x.z;
mat[1][2] = t.y.z;
mat[2][0] = t.z.x;
mat[2][1] = t.z.y;
#endif
}
Transform a matrix in a basilisk tensor
pseudo_t pseudo_t_from_matrix (double mat[dimension][dimension]) {
pseudo_t t;
t.x.x = mat[0][0];
#if dimension > 1
t.y.y = mat[1][1];
t.x.y = mat[0][1];
t.y.x = mat[1][0];
#endif
#if dimension > 2
t.z.z = mat[2][2];
t.x.z = mat[0][2];
t.y.z = mat[1][2];
t.z.x = mat[2][0];
t.z.y = mat[2][1];
#endif
return t;
}
Create diagonal tensor from a vector
Useful to create the diagonal matrix containing the eigen values of a tensor, to use tensor multiplication : M = R\Lambda R^{-1}.
pseudo_t diagonal_pseudo_t_from_pseudo_v (pseudo_v v) {
pseudo_t tmp;
foreach_dimension() {
tmp.x.x = v.x;
#if dimension > 1
tmp.x.y = 0.;
#endif
#if dimension > 2
tmp.x.z = 0.;
#endif
}
return tmp;
}
Trace of a tensor
Compute the trace of a pseudo-tensor.
double trace_pseudo_t (pseudo_t t) {
double vtrace = 0.;
foreach_dimension()
vtrace += t.x.x;
return vtrace;
}