|
@@ -0,0 +1,1097 @@
|
|
|
+#include <math.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <string.h>
|
|
|
+#include <complex.h>
|
|
|
+#include <omp.h>
|
|
|
+#include "diff_eq_system.h"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void ephi(double phi, double* vector) {
|
|
|
+ vector[0] = -sin(phi);
|
|
|
+ vector[1] = cos(phi);
|
|
|
+ vector[2] = 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void etheta(double phi, double theta, double* vector) {
|
|
|
+ vector[0] = cos(phi) * cos(theta);
|
|
|
+ vector[1] = sin(phi) * cos(theta);
|
|
|
+ vector[2] = -sin(theta);
|
|
|
+}
|
|
|
+
|
|
|
+void sph_to_cart(double theta, double phi, double* cartesian) {
|
|
|
+ cartesian[0] = cos(phi) * sin(theta);
|
|
|
+ cartesian[1] = sin(phi) * sin(theta);
|
|
|
+ cartesian[2] = cos(theta);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void cross_product(double* vector0, double* vector1, double* result) {
|
|
|
+
|
|
|
+ result[0] = vector0[1] * vector1[2] - vector0[2] * vector1[1];
|
|
|
+ result[1] = vector0[2] * vector1[0] - vector0[0] * vector1[2];
|
|
|
+ result[2] = vector0[0] * vector1[1] - vector0[1] * vector1[0];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void lattice_base(int n, double* thetas, double* phis, double* basis) {
|
|
|
+ // Returns n x 3 x 3 array of local lattice bases, serves as transformation from global to local basis
|
|
|
+ double cos_phi, sin_phi, cos_theta, sin_theta;
|
|
|
+ for (int i=0; i<n; i++) {
|
|
|
+ cos_phi = cos(phis[i]);
|
|
|
+ sin_phi = sin(phis[i]);
|
|
|
+ cos_theta = cos(thetas[i]);
|
|
|
+ sin_theta = sin(thetas[i]);
|
|
|
+ basis[9*i + 0] = cos_phi * sin_theta;
|
|
|
+ basis[9*i + 1] = sin_theta * sin_phi;
|
|
|
+ basis[9*i + 2] = cos_theta;
|
|
|
+ basis[9*i + 3] = cos_theta * cos_phi;
|
|
|
+ basis[9*i + 4] = cos_theta * sin_phi;
|
|
|
+ basis[9*i + 5] = -sin_theta;
|
|
|
+ basis[9*i + 6] = -sin_phi;
|
|
|
+ basis[9*i + 7] = cos_phi;
|
|
|
+ basis[9*i + 8] = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void rodrigues_rotation(double* vec, double angle, double* axis, double* result) {
|
|
|
+ double cos_angle = cos(angle);
|
|
|
+ double sin_angle = sin(angle);
|
|
|
+ double scalar;
|
|
|
+ double cross[3];
|
|
|
+
|
|
|
+ cross_product(axis, vec, cross);
|
|
|
+ scalar = axis[0]*vec[0] + axis[1]*vec[1] + axis[2]*vec[2];
|
|
|
+
|
|
|
+ result[0] = vec[0] * cos_angle + cross[0] * sin_angle + axis[0] * scalar * (1 - cos_angle);
|
|
|
+ result[1] = vec[1] * cos_angle + cross[1] * sin_angle + axis[1] * scalar * (1 - cos_angle);
|
|
|
+ result[2] = vec[2] * cos_angle + cross[2] * sin_angle + axis[2] * scalar * (1 - cos_angle);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void x_to_z_transf(int n, double* vectors) {
|
|
|
+ // rotation of all vectors in array for pi/2 around y-axis (transformation x -> z and z -> -x)
|
|
|
+ double new_x;
|
|
|
+ double new_z;
|
|
|
+
|
|
|
+ for (int i=0; i<n; i++) {
|
|
|
+ new_x = -vectors[3*i+2];
|
|
|
+ new_z = vectors[3*i];
|
|
|
+ vectors[3*i] = new_x;
|
|
|
+ vectors[3*i+2] = new_z;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void inverse_x_to_z_transformation(int n, double* vectors) {
|
|
|
+ // rotation of all vectors in array for -pi/2 around y-axis (transformation x -> -z and z -> x)
|
|
|
+ double new_x;
|
|
|
+ double new_z;
|
|
|
+
|
|
|
+ for (int i=0; i<n; i++) {
|
|
|
+ new_x = vectors[3*i+2];
|
|
|
+ new_z = -vectors[3*i];
|
|
|
+ vectors[3*i] = new_x;
|
|
|
+ vectors[3*i+2] = new_z;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double complex stereogr_proj(double theta, double phi) {
|
|
|
+ return cexp(I * phi) / tan(theta / 2.);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void psi_avg(double* thetas, double* phis, double alpha0, double* result) {
|
|
|
+ // Minimizing defect orientation
|
|
|
+ double complex ln_sum;
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ ln_sum = 0;
|
|
|
+ for (int j=0; j<4; j++) {
|
|
|
+ if (j != i) {
|
|
|
+ ln_sum += clog(stereogr_proj(thetas[i], phis[i]) - stereogr_proj(thetas[j], phis[j]));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result[i] = phis[i] - 2 * alpha0 - cimag(ln_sum);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double cos_beta_ij(double theta_i, double phi_i, double theta_j, double phi_j) {
|
|
|
+ // Angular distance between defects
|
|
|
+ return cos(theta_i) * cos(theta_j) + sin(theta_i) * sin(theta_j) * cos(phi_i - phi_j);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double dcos_beta_dtheta(double theta_i, double phi_i, double theta_j, double phi_j) {
|
|
|
+ // d(cos beta_ki) / d theta_k
|
|
|
+ return -sin(theta_i) * cos(theta_j) + cos(theta_i) * sin(theta_j) * cos(phi_i - phi_j);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double dcos_beta_dphi(double theta_i, double phi_i, double theta_j, double phi_j) {
|
|
|
+ // d(cos beta_ki) / d phi_k
|
|
|
+ return -sin(theta_i) * sin(theta_j) * sin(phi_i - phi_j);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double free_energy(double* thetas, double* phis, double* delta_psi, double chi) {
|
|
|
+ // elastic free energy
|
|
|
+ double en = 0;
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ en += chi * delta_psi[i] * delta_psi[i];
|
|
|
+ for (int j=0; j<4; j++) {
|
|
|
+ if (j != i) {
|
|
|
+ en += -clog(1 - cos_beta_ij(thetas[i], phis[i], thetas[j], phis[j]));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return en;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double free_energy_pos(double* thetas, double* phis) {
|
|
|
+ // lastic free energy
|
|
|
+ double en = 0;
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ for (int j=0; j<4; j++) {
|
|
|
+ if (j != i) {
|
|
|
+ en += -clog(1 - cos_beta_ij(thetas[i], phis[i], thetas[j], phis[j]));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return en;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double free_energy_orient(double* delta_psi, double chi) {
|
|
|
+ // lastic free energy
|
|
|
+ double en = 0;
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ en += chi * delta_psi[i] * delta_psi[i];
|
|
|
+ }
|
|
|
+ return en;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void df_dtheta(double* thetas, double* phis, double* delta_psi, double chi, double* derivatives) {
|
|
|
+ // Free energy derivative over theta_k
|
|
|
+ for (int k=0; k<4; k++) {
|
|
|
+ derivatives[k] = 0;
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ if (i != k) {
|
|
|
+ derivatives[k] += 2 * (
|
|
|
+ dcos_beta_dtheta(thetas[k], phis[k], thetas[i], phis[i]) /
|
|
|
+ (1 - cos_beta_ij(thetas[k], phis[k], thetas[i], phis[i])) -
|
|
|
+ chi * (delta_psi[k] + delta_psi[i]) / sin(thetas[k]) *
|
|
|
+ cimag(stereogr_proj(thetas[k], phis[k]) /
|
|
|
+ (stereogr_proj(thetas[k], phis[k]) - stereogr_proj(thetas[i], phis[i])))
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void df_dphi(double* thetas, double* phis, double* delta_psi, double chi, double* derivatives) {
|
|
|
+ // Free energy derivative over phi_k
|
|
|
+ for (int k=0; k<4; k++) {
|
|
|
+ derivatives[k] = 0;
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ if (i != k) {
|
|
|
+ derivatives[k] += 2 * (
|
|
|
+ dcos_beta_dphi(thetas[k], phis[k], thetas[i], phis[i]) /
|
|
|
+ (1 - cos_beta_ij(thetas[k], phis[k], thetas[i], phis[i])) +
|
|
|
+ chi * (
|
|
|
+ (delta_psi[k] + delta_psi[i]) *
|
|
|
+ creal(stereogr_proj(thetas[k], phis[k]) /
|
|
|
+ (stereogr_proj(thetas[k], phis[k]) - stereogr_proj(thetas[i], phis[i]))) -
|
|
|
+ delta_psi[k] / 3.
|
|
|
+ )
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double df_dalpha0(double* delta_psi, double chi) {
|
|
|
+ // Free energy derivative over alpha0
|
|
|
+ double derivative = 0;
|
|
|
+ for (int k=0; k<4; k++) {
|
|
|
+ derivative += 4 * chi * delta_psi[k];
|
|
|
+ }
|
|
|
+ return derivative;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void df_dpsi(double* delta_psi, double chi, double* derivatives) {
|
|
|
+ // Free energy derivative over psi_k
|
|
|
+ for (int k=0; k<4; k++) {
|
|
|
+ derivatives[k] = 2 * chi * delta_psi[k];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void eq_system(double* variables, double nu, double chi, double xi_rot, double xi_alpha, double* dy_dt) {
|
|
|
+ // System of differential equations. y=[theta_k, phi_k, psi_k, alpha0] (len(y)=13)
|
|
|
+ double thetas[4];
|
|
|
+ double phis[4];
|
|
|
+ double psis[4];
|
|
|
+ double delta_psi[4];
|
|
|
+ double alpha0 = variables[12];
|
|
|
+ double average_psi[4];
|
|
|
+
|
|
|
+ psi_avg(&variables[0], &variables[4], alpha0, average_psi);
|
|
|
+
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ thetas[i] = variables[i];
|
|
|
+ phis[i] = variables[4 + i];
|
|
|
+ psis[i] = variables[8 + i];
|
|
|
+ delta_psi[i] = psis[i] - average_psi[i];
|
|
|
+ delta_psi[i] -= M_PI * (2 * floor((delta_psi[i]+M_PI) / (2 * M_PI))); // map into the interval [-pi , pi]
|
|
|
+ }
|
|
|
+
|
|
|
+ double theta_deriv[4];
|
|
|
+ double phi_deriv[4];
|
|
|
+ double psi_deriv[4];
|
|
|
+ double alpha0_deriv = df_dalpha0(delta_psi, chi);
|
|
|
+
|
|
|
+ df_dtheta(thetas, phis, delta_psi, 0, theta_deriv); // chi=0 for stability
|
|
|
+ df_dphi(thetas, phis, delta_psi, 0, phi_deriv); // chi=0 for stability
|
|
|
+ df_dpsi(delta_psi, chi, psi_deriv);
|
|
|
+
|
|
|
+ double complex compl_velocity_conj[4];
|
|
|
+
|
|
|
+ for (int k=0; k<4; k++) {
|
|
|
+ // dtheta/dt
|
|
|
+ dy_dt[k] = -theta_deriv[k] + nu * cos(psis[k]);
|
|
|
+ // dphi/dt
|
|
|
+ dy_dt[4 + k] = -phi_deriv[k] / (sin(thetas[k]) * sin(thetas[k])) + nu * sin(psis[k]) / sin(thetas[k]);
|
|
|
+ // dpsi/dt
|
|
|
+ dy_dt[8 + k] = -1 / xi_rot * psi_deriv[k] - cos(thetas[k]) * dy_dt[4 + k];
|
|
|
+ // conjugated complex velocity
|
|
|
+ compl_velocity_conj[k] = dy_dt[k] - I * sin(thetas[k]) * dy_dt[4 + k];
|
|
|
+ }
|
|
|
+
|
|
|
+ // dalpha0/dt
|
|
|
+ double inner_summand;
|
|
|
+ double outer_sum=0;
|
|
|
+ for (int k=0; k<4; k++) {
|
|
|
+ inner_summand = 0;
|
|
|
+ for (int j=0; j<4; j++) {
|
|
|
+ if (j != k) {
|
|
|
+ inner_summand += cimag( stereogr_proj(thetas[k], phis[k]) *
|
|
|
+ compl_velocity_conj[k] /
|
|
|
+ (stereogr_proj(thetas[k], phis[k]) - stereogr_proj(thetas[j], phis[j])) );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ outer_sum += 1 / 8. * ( (1 + cos(thetas[k])) * dy_dt[4 + k] + 2 / sin(thetas[k]) * inner_summand );
|
|
|
+ }
|
|
|
+ dy_dt[12] = -1 / xi_alpha * alpha0_deriv + outer_sum;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void eq_system_reduced(double* variables, double nu, double tau, double* dy_dt) {
|
|
|
+ // System of reduced differential equations. y=[Theta, Phi, Psi, Delta_Psi, phi1] (len(y)=5)
|
|
|
+
|
|
|
+ double theta = variables[0];
|
|
|
+ double phi = variables[1];
|
|
|
+ double avg_psi = variables[2];
|
|
|
+ double delta_psi = variables[3]; // - M_PI * (2 * floor((variables[3] + M_PI) / (2 * M_PI)));
|
|
|
+ double phi1 = variables[4];
|
|
|
+
|
|
|
+ double sin_theta = sin(theta);
|
|
|
+ double cos_theta = cos(theta);
|
|
|
+ double tan_theta = tan(theta);
|
|
|
+ double sin_phi = sin(phi);
|
|
|
+ double cos_phi = cos(phi);
|
|
|
+
|
|
|
+ // dtheta/dt
|
|
|
+ dy_dt[0] = nu * cos(avg_psi + delta_psi) + 2 * (4 - 8 * pow(sin_theta, 2) + 3 * pow(sin_theta, 4) * pow(sin_phi, 2)) /
|
|
|
+ (tan_theta * ((1 + pow(cos_theta, 2)) * (1 + pow(cos_theta, 2)) - pow(sin_theta, 4) * pow(cos_phi, 2)));
|
|
|
+ // dphi/dt
|
|
|
+ dy_dt[1] = -2 * nu * sin(avg_psi) * cos(delta_psi) / sin_theta + 8 * pow(sin_theta, 2) * sin_phi * cos_phi /
|
|
|
+ ((1 + pow(cos_theta, 2)) * (1 + pow(cos_theta, 2)) - pow(sin_theta, 4) * pow(cos_phi, 2));
|
|
|
+ // d(avg_psi)/dt
|
|
|
+ dy_dt[2] = cos_theta / 2 * dy_dt[1];
|
|
|
+ // d(delta_psi)/dt
|
|
|
+ dy_dt[3] = -nu * cos(avg_psi) * sin(delta_psi) / tan_theta - delta_psi / tau;
|
|
|
+ // dphi1/dt
|
|
|
+ dy_dt[4] = nu / sin_theta * cos(avg_psi) * sin(delta_psi) - dy_dt[1] / 2;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void propagate_trajectories_block(double* variables, double nu, double chi, double xi_rot, double xi_alpha,
|
|
|
+ double dt, int num_steps) {
|
|
|
+
|
|
|
+ double k1[13];
|
|
|
+ double k2[13];
|
|
|
+ double intermed[13];
|
|
|
+ double t = 0;
|
|
|
+
|
|
|
+ for (int i=0; i<num_steps; i++) {
|
|
|
+
|
|
|
+ eq_system(variables, nu, chi, xi_rot, xi_alpha, k1);
|
|
|
+
|
|
|
+ for (int j=0; j<13; j++) {
|
|
|
+ intermed[j] = variables[j] + 2. / 3. * dt * k1[j];
|
|
|
+ }
|
|
|
+
|
|
|
+ eq_system(intermed, nu, chi, xi_rot, xi_alpha, k2);
|
|
|
+
|
|
|
+ for (int j=0; j<13; j++) {
|
|
|
+ variables[j] = variables[j] + dt * (1.0 / 4.0) * (k1[j] + 3 * k2[j]);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void propagate_trajectories(double* variables0, double nu, double chi, double xi_rot, double xi_alpha,
|
|
|
+ double t_span, double dt, int num_save,
|
|
|
+ double* results, double* timepoints, int* nfev) {
|
|
|
+
|
|
|
+ // Should be len(results) / 13 = len(timepoints) = num_save
|
|
|
+
|
|
|
+ double t = 0;
|
|
|
+ int save_interval = round(t_span / (dt * (num_save - 1)));
|
|
|
+ dt = t_span / (save_interval * (num_save - 1));
|
|
|
+
|
|
|
+ nfev[0] = 2 * save_interval * (num_save - 1);
|
|
|
+
|
|
|
+ for (int i=0; i<num_save; i++) {
|
|
|
+
|
|
|
+ timepoints[i] = t;
|
|
|
+ for (int j=0; j<13; j++) {
|
|
|
+ results[13 * i + j] = variables0[j];
|
|
|
+ // printf("%f ", results[13 * i + j]);
|
|
|
+ }
|
|
|
+ // printf("\n");
|
|
|
+
|
|
|
+ if (i < num_save - 1) {
|
|
|
+ propagate_trajectories_block(variables0, nu, chi, xi_rot, xi_alpha, dt, save_interval);
|
|
|
+ t += save_interval * dt;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void propagate_reduced_trajectories_block(double* variables, double nu, double tau, double dt, int num_steps) {
|
|
|
+
|
|
|
+ double k1[5];
|
|
|
+ double k2[5];
|
|
|
+ double intermed[5];
|
|
|
+ double t = 0;
|
|
|
+
|
|
|
+ for (int i=0; i<num_steps; i++) {
|
|
|
+
|
|
|
+ eq_system_reduced(variables, nu, tau, k1);
|
|
|
+
|
|
|
+ for (int j=0; j<5; j++) {
|
|
|
+ intermed[j] = variables[j] + 2. / 3. * dt * k1[j];
|
|
|
+ }
|
|
|
+
|
|
|
+ eq_system_reduced(intermed, nu, tau, k2);
|
|
|
+
|
|
|
+ for (int j=0; j<5; j++) {
|
|
|
+ variables[j] = variables[j] + dt * (1.0 / 4.0) * (k1[j] + 3 * k2[j]);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void propagate_reduced_trajectories(double* variables0, double nu, double tau,
|
|
|
+ double t_span, double dt, int num_save,
|
|
|
+ double* results, double* timepoints, int* nfev) {
|
|
|
+
|
|
|
+ // Should be len(results) / 5 = len(timepoints) = num_save
|
|
|
+
|
|
|
+ double t = 0;
|
|
|
+ int save_interval = round(t_span / (dt * (num_save - 1)));
|
|
|
+ dt = t_span / (save_interval * (num_save - 1));
|
|
|
+
|
|
|
+ nfev[0] = 2 * save_interval * (num_save - 1);
|
|
|
+
|
|
|
+ for (int i=0; i<num_save; i++) {
|
|
|
+
|
|
|
+ timepoints[i] = t;
|
|
|
+ for (int j=0; j<5; j++) {
|
|
|
+ results[5 * i + j] = variables0[j];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i < num_save - 1) {
|
|
|
+ propagate_reduced_trajectories_block(variables0, nu, tau, dt, save_interval);
|
|
|
+ t += save_interval * dt;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void eq_system_drag(double* variables, double* grad_theta, double* grad_phi,
|
|
|
+ double nu, double chi, double mu, double xi_rot, double xi_alpha, double* dy_dt) {
|
|
|
+ // System of differential equations. y=[theta_k, phi_k, psi_k, alpha0] (len(y)=13)
|
|
|
+ // grad_theta contains the etheta component of velocity gradient in radial direction
|
|
|
+ // grad_phi contains the ephi component of velocity gradient in radial direction
|
|
|
+
|
|
|
+
|
|
|
+ double thetas[4];
|
|
|
+ double phis[4];
|
|
|
+ double psis[4];
|
|
|
+ double delta_psi[4];
|
|
|
+ double alpha0 = variables[12];
|
|
|
+ double average_psi[4];
|
|
|
+
|
|
|
+ psi_avg(&variables[0], &variables[4], alpha0, average_psi);
|
|
|
+
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ thetas[i] = variables[i];
|
|
|
+ phis[i] = variables[4 + i];
|
|
|
+ psis[i] = variables[8 + i];
|
|
|
+ delta_psi[i] = psis[i] - average_psi[i];
|
|
|
+ delta_psi[i] -= M_PI * (2 * floor((delta_psi[i]+M_PI) / (2 * M_PI))); // map into the interval [-pi , pi]
|
|
|
+ }
|
|
|
+
|
|
|
+ double theta_deriv[4];
|
|
|
+ double phi_deriv[4];
|
|
|
+ double psi_deriv[4];
|
|
|
+ double alpha0_deriv = df_dalpha0(delta_psi, chi);
|
|
|
+
|
|
|
+ df_dtheta(thetas, phis, delta_psi, 0, theta_deriv); // chi=0 for stability
|
|
|
+ df_dphi(thetas, phis, delta_psi, 0, phi_deriv); // chi=0 for stability
|
|
|
+ df_dpsi(delta_psi, chi, psi_deriv);
|
|
|
+
|
|
|
+ double complex compl_velocity_conj[4];
|
|
|
+
|
|
|
+ for (int k=0; k<4; k++) {
|
|
|
+ // dtheta/dt
|
|
|
+ dy_dt[k] = -theta_deriv[k] + nu * cos(psis[k]) - mu * grad_theta[k];
|
|
|
+ // dphi/dt
|
|
|
+ dy_dt[4 + k] = -phi_deriv[k] / (sin(thetas[k]) * sin(thetas[k])) +
|
|
|
+ (nu * sin(psis[k]) - mu * grad_phi[k]) / sin(thetas[k]);
|
|
|
+ // dpsi/dt
|
|
|
+ dy_dt[8 + k] = -1 / xi_rot * psi_deriv[k] - cos(thetas[k]) * dy_dt[4 + k];
|
|
|
+ // conjugated complex velocity
|
|
|
+ compl_velocity_conj[k] = dy_dt[k] - I * sin(thetas[k]) * dy_dt[4 + k];
|
|
|
+ }
|
|
|
+
|
|
|
+ // dalpha0/dt
|
|
|
+ double inner_summand;
|
|
|
+ double outer_sum=0;
|
|
|
+ for (int k=0; k<4; k++) {
|
|
|
+ inner_summand = 0;
|
|
|
+ for (int j=0; j<4; j++) {
|
|
|
+ if (j != k) {
|
|
|
+ inner_summand += cimag( stereogr_proj(thetas[k], phis[k]) *
|
|
|
+ compl_velocity_conj[k] /
|
|
|
+ (stereogr_proj(thetas[k], phis[k]) - stereogr_proj(thetas[j], phis[j])) );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ outer_sum += 1 / 8. * ( (1 + cos(thetas[k])) * dy_dt[4 + k] + 2 / sin(thetas[k]) * inner_summand );
|
|
|
+ }
|
|
|
+ dy_dt[12] = -1 / xi_alpha * alpha0_deriv + outer_sum;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void propagate_trajectories_step_drag(double* variables, double* grad_theta, double* grad_phi,
|
|
|
+ double nu, double chi, double mu, double xi_rot, double xi_alpha,
|
|
|
+ double dt) {
|
|
|
+ // One step of propagation with viscous drag
|
|
|
+
|
|
|
+ double k1[13];
|
|
|
+ double k2[13];
|
|
|
+ double intermed[13];
|
|
|
+ double t = 0;
|
|
|
+
|
|
|
+ eq_system_drag(variables, grad_theta, grad_phi, nu, chi, mu, xi_rot, xi_alpha, k1);
|
|
|
+
|
|
|
+ for (int j=0; j<13; j++) {
|
|
|
+ intermed[j] = variables[j] + 2. / 3. * dt * k1[j];
|
|
|
+ }
|
|
|
+
|
|
|
+ eq_system_drag(intermed, grad_theta, grad_phi, nu, chi, mu, xi_rot, xi_alpha, k2);
|
|
|
+
|
|
|
+ for (int j=0; j<13; j++) {
|
|
|
+ variables[j] = variables[j] + dt * (1.0 / 4.0) * (k1[j] + 3 * k2[j]);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void gaussian_belt(double* variables, double* grad_theta, double* grad_phi, double* velocity) {
|
|
|
+ // test function for generating the gradient field as a belt around the equator
|
|
|
+ double sigma = 0.3;
|
|
|
+ double magnitude;
|
|
|
+ double in_exponent;
|
|
|
+
|
|
|
+ for (int i=0; i<4; i++) {
|
|
|
+ in_exponent = (variables[i] - M_PI / 2) / sigma;
|
|
|
+ magnitude = velocity[i] / (sigma * sqrt(2 * M_PI)) * exp(-0.5 * in_exponent * in_exponent);
|
|
|
+ grad_theta[i] = magnitude * cos(variables[8 + i]);
|
|
|
+ grad_phi[i] = magnitude * sin(variables[8 + i]);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void propagate_trajectories_drag(double* variables0, double nu, double chi, double mu,
|
|
|
+ double xi_rot, double xi_alpha,
|
|
|
+ double t_span, double dt, int num_save, double* results,
|
|
|
+ double* timepoints, int* nfev) {
|
|
|
+
|
|
|
+ // Should be len(results) / 13 = len(timepoints) = num_save
|
|
|
+
|
|
|
+ double t = 0;
|
|
|
+ int save_interval = round(t_span / (dt * (num_save - 1)));
|
|
|
+ dt = t_span / (save_interval * (num_save - 1));
|
|
|
+
|
|
|
+ double grad_theta[4];
|
|
|
+ double grad_phi[4];
|
|
|
+
|
|
|
+ nfev[0] = 2 * save_interval * (num_save - 1);
|
|
|
+
|
|
|
+ for (int i=0; i<num_save; i++) {
|
|
|
+
|
|
|
+ timepoints[i] = t;
|
|
|
+ for (int j=0; j<13; j++) {
|
|
|
+ results[13 * i + j] = variables0[j];
|
|
|
+ // printf("%f ", results[13 * i + j]);
|
|
|
+ }
|
|
|
+ // printf("\n");
|
|
|
+
|
|
|
+ double velocity[4];
|
|
|
+ for (int i=0; i<4; i++) {velocity[i] = nu;}
|
|
|
+
|
|
|
+ if (i < num_save - 1) {
|
|
|
+ for (int k=0; k<save_interval; k++) {
|
|
|
+ gaussian_belt(variables0, grad_theta, grad_phi, velocity);
|
|
|
+ propagate_trajectories_step_drag(variables0, grad_theta, grad_phi, nu, chi, mu, xi_rot, xi_alpha, dt);
|
|
|
+ }
|
|
|
+ t += save_interval * dt;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double psi_around_defect(double theta, double phi, double psi,
|
|
|
+ double theta_defect, double phi_defect, double psi_defect, double radial_distance) {
|
|
|
+ /*
|
|
|
+ Translation of defect velocity direction to surrounding points. Does interpolation between
|
|
|
+ defect direction psi_theta and previously calculated direction psi
|
|
|
+ */
|
|
|
+
|
|
|
+ double coord_defect[3];
|
|
|
+ double coord[3];
|
|
|
+ double axis[3];
|
|
|
+ double axis_normalization;
|
|
|
+ double cos_angle;
|
|
|
+ double angle;
|
|
|
+
|
|
|
+ sph_to_cart(theta_defect, phi_defect, coord_defect);
|
|
|
+ sph_to_cart(theta, phi, coord);
|
|
|
+ cross_product(coord_defect, coord, axis);
|
|
|
+ axis_normalization = sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
|
|
|
+ axis[0] = axis[0] / axis_normalization;
|
|
|
+ axis[1] = axis[1] / axis_normalization;
|
|
|
+ axis[2] = axis[2] / axis_normalization;
|
|
|
+
|
|
|
+ cos_angle = cos_beta_ij(theta_defect, phi_defect, theta, phi);
|
|
|
+ angle = acos(cos_angle);
|
|
|
+
|
|
|
+ double defect_dir[3];
|
|
|
+ double vec_phi_defect[3];
|
|
|
+ double vec_theta_defect[3];
|
|
|
+ double cos_psi_defect = cos(psi_defect);
|
|
|
+ double sin_psi_defect = sin(psi_defect);
|
|
|
+
|
|
|
+ ephi(phi_defect, vec_phi_defect);
|
|
|
+ etheta(phi_defect, theta_defect, vec_theta_defect);
|
|
|
+
|
|
|
+ defect_dir[0] = cos_psi_defect * vec_theta_defect[0] + sin_psi_defect * vec_phi_defect[0];
|
|
|
+ defect_dir[1] = cos_psi_defect * vec_theta_defect[1] + sin_psi_defect * vec_phi_defect[1];
|
|
|
+ defect_dir[2] = cos_psi_defect * vec_theta_defect[2] + sin_psi_defect * vec_phi_defect[2];
|
|
|
+
|
|
|
+ // propagated (translated) defect direction
|
|
|
+ double propag_dir[3];
|
|
|
+ rodrigues_rotation(defect_dir, angle, axis, propag_dir);
|
|
|
+
|
|
|
+ // local coordiantes in he point of interest
|
|
|
+ double vec_phi[3];
|
|
|
+ double vec_theta[3];
|
|
|
+ double psi_propag;
|
|
|
+
|
|
|
+ ephi(phi, vec_phi);
|
|
|
+ etheta(phi, theta, vec_theta);
|
|
|
+
|
|
|
+ // linear interpolation if radial distance is between 0.5 and 1 in units of rho
|
|
|
+ // could be done with rodrigues but we used new_v = (a*v0 + (1-a)*v1) * renormalization
|
|
|
+ double renormalization; // for the interpolated vector to have the same length
|
|
|
+ double interp_factor;
|
|
|
+ double cos_psi = cos(psi);
|
|
|
+ double sin_psi = sin(psi);
|
|
|
+ double previous_dir[3]; // v0 (if as v1, we consider the propagated defect direction)
|
|
|
+ double v0_v1;
|
|
|
+
|
|
|
+ if (radial_distance > 0.5 && radial_distance < 1) {
|
|
|
+ interp_factor = 2 * (1 - radial_distance);
|
|
|
+ previous_dir[0] = cos_psi * vec_theta[0] + sin_psi * vec_phi[0];
|
|
|
+ previous_dir[1] = cos_psi * vec_theta[1] + sin_psi * vec_phi[1];
|
|
|
+ previous_dir[2] = cos_psi * vec_theta[2] + sin_psi * vec_phi[2];
|
|
|
+
|
|
|
+ v0_v1 = previous_dir[0] * propag_dir[0] + previous_dir[1] * propag_dir[1] + previous_dir[2] * propag_dir[2];
|
|
|
+ renormalization = sqrt(1 / (interp_factor*interp_factor + (1 - interp_factor) * (1 - interp_factor) +
|
|
|
+ 2 * interp_factor * (1 - interp_factor) * v0_v1));
|
|
|
+
|
|
|
+ propag_dir[0] = (interp_factor * propag_dir[0] + (1 - interp_factor) * previous_dir[0]) * renormalization;
|
|
|
+ propag_dir[1] = (interp_factor * propag_dir[1] + (1 - interp_factor) * previous_dir[1]) * renormalization;
|
|
|
+ propag_dir[2] = (interp_factor * propag_dir[2] + (1 - interp_factor) * previous_dir[2]) * renormalization;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // projection to local coordinate axes
|
|
|
+ double sin_psi_propag = propag_dir[0] * vec_phi[0] + propag_dir[1] * vec_phi[1] + propag_dir[2] * vec_phi[2];
|
|
|
+ double cos_psi_propag = propag_dir[0] * vec_theta[0] + propag_dir[1] * vec_theta[1] + propag_dir[2] * vec_theta[2];
|
|
|
+
|
|
|
+ psi_propag = atan2(sin_psi_propag, cos_psi_propag);
|
|
|
+
|
|
|
+ return psi_propag;
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void velocity_field(double* defect_config, int n, double* lattice_thetas, double* lattice_phis,
|
|
|
+ double nu, double rho, double* velocities) {
|
|
|
+
|
|
|
+ // printf("Slow C function.\n");
|
|
|
+
|
|
|
+ double psi_bar[4];
|
|
|
+ psi_avg(&defect_config[0], &defect_config[4], defect_config[12], psi_bar);
|
|
|
+
|
|
|
+ double complex z_defect[4];
|
|
|
+ double delta_psi[4];
|
|
|
+ for (int i=0; i< 4; i++) {
|
|
|
+ z_defect[i] = stereogr_proj(defect_config[i], defect_config[4+i]);
|
|
|
+ delta_psi[i] = defect_config[8 + i] - psi_bar[i];
|
|
|
+ delta_psi[i] = delta_psi[i] - 2 * M_PI * floor((delta_psi[i] + M_PI) / (2 * M_PI));
|
|
|
+ }
|
|
|
+
|
|
|
+ double alpha;
|
|
|
+ double dalpha_dphi, dalpha_dtheta;
|
|
|
+ double u_theta, u_phi;
|
|
|
+
|
|
|
+ double complex sum_in_alpha_eq;
|
|
|
+ double sum_in_alpha_tilde;
|
|
|
+ double cos_beta_defect;
|
|
|
+ double beta_defect;
|
|
|
+ double dbeta_dphi, dbeta_dtheta;
|
|
|
+ double alpha_eq, alpha_tilde;
|
|
|
+ double dalpha_eq_dphi, dalpha_eq_dtheta;
|
|
|
+ double dalpha_tilde_dphi, dalpha_tilde_dtheta;
|
|
|
+ double complex sum_in_dalpha_eq;
|
|
|
+ int cutoff;
|
|
|
+ int direction_cutoff;
|
|
|
+ double new_psi; // for propagating flow direction near the defects
|
|
|
+ double defect_dist; // distance from the closest defect
|
|
|
+ double velocity_norm;
|
|
|
+
|
|
|
+ double ln1_rho = log(1/rho);
|
|
|
+ double vec_phi[3];
|
|
|
+ double vec_theta[3];
|
|
|
+
|
|
|
+ double complex z_point;
|
|
|
+
|
|
|
+ for (int i=0; i<n; i++) {
|
|
|
+ sum_in_alpha_eq = 0;
|
|
|
+ sum_in_alpha_tilde = 0;
|
|
|
+ sum_in_dalpha_eq = 0;
|
|
|
+ cutoff = 0;
|
|
|
+ direction_cutoff = 0;
|
|
|
+ defect_dist = 0;
|
|
|
+
|
|
|
+ dalpha_tilde_dphi = 0;
|
|
|
+ dalpha_tilde_dtheta = 0;
|
|
|
+
|
|
|
+ // numerical correction for the lattice point exactly at the north pole (needed for the stereographic projection).
|
|
|
+ if (lattice_thetas[i] < 1.0E-15) {
|
|
|
+ lattice_thetas[i] = 1.0E-15;
|
|
|
+ }
|
|
|
+
|
|
|
+ z_point = stereogr_proj(lattice_thetas[i], lattice_phis[i]);
|
|
|
+
|
|
|
+ for (int j=0; j<4; j++) {
|
|
|
+
|
|
|
+ cos_beta_defect = cos_beta_ij(lattice_thetas[i], lattice_phis[i], defect_config[j], defect_config[4 + j]);
|
|
|
+ beta_defect = acos(cos_beta_defect);
|
|
|
+ if (beta_defect < 1.25 * rho) {
|
|
|
+ cutoff = 1; // possible velocity cutoff near the defects (fixed defect speed, diverges otherwise)
|
|
|
+ }
|
|
|
+ if (beta_defect < rho) {
|
|
|
+ defect_dist = beta_defect / rho; // distance from the defect in units of rho
|
|
|
+ direction_cutoff = j + 1; // possible direction cutoff near the defects
|
|
|
+ }
|
|
|
+
|
|
|
+ dbeta_dphi = -dcos_beta_dphi(lattice_thetas[i], lattice_phis[i], defect_config[j], defect_config[4 + j])
|
|
|
+ / sqrt(1 - cos_beta_defect * cos_beta_defect);
|
|
|
+ dbeta_dtheta = -dcos_beta_dtheta(lattice_thetas[i], lattice_phis[i], defect_config[j], defect_config[4 + j])
|
|
|
+ / sqrt(1 - cos_beta_defect * cos_beta_defect);
|
|
|
+
|
|
|
+ sum_in_alpha_eq += clog(z_point - z_defect[j]);
|
|
|
+ sum_in_alpha_tilde += delta_psi[j] * (log(1 / sin(beta_defect / 2)) - 1);
|
|
|
+ sum_in_dalpha_eq += z_point / (z_point - z_defect[j]);
|
|
|
+
|
|
|
+ dalpha_tilde_dphi += -delta_psi[j] / (4 * ln1_rho * tan(beta_defect / 2)) * dbeta_dphi;
|
|
|
+ dalpha_tilde_dtheta += -delta_psi[j] / (4 * ln1_rho * tan(beta_defect / 2)) * dbeta_dtheta;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ alpha_eq = lattice_phis[i] - defect_config[12] - 0.5 * cimag(sum_in_alpha_eq);
|
|
|
+ alpha_tilde = 1 / (2 * ln1_rho) * sum_in_alpha_tilde;
|
|
|
+ alpha = alpha_eq + alpha_tilde;
|
|
|
+
|
|
|
+ dalpha_eq_dphi = 1 - 0.5 * creal(sum_in_dalpha_eq);
|
|
|
+ dalpha_eq_dtheta = 1 / (2 * sin(lattice_thetas[i])) * cimag(sum_in_dalpha_eq);
|
|
|
+
|
|
|
+ dalpha_dphi = dalpha_eq_dphi + dalpha_tilde_dphi;
|
|
|
+ dalpha_dtheta = dalpha_eq_dtheta + dalpha_tilde_dtheta;
|
|
|
+
|
|
|
+ // averaged delta psi for higher accuracy of renormalization
|
|
|
+ double avg_delta_psi = 0.25 * (fabs(delta_psi[0]) + fabs(delta_psi[1]) + fabs(delta_psi[2]) + fabs(delta_psi[3]));
|
|
|
+
|
|
|
+ double normalization = 2 * rho * nu / sqrt(1 + (avg_delta_psi/ln1_rho) * (avg_delta_psi/ln1_rho));
|
|
|
+
|
|
|
+ u_theta = normalization * (sin(2 * alpha) * dalpha_dtheta
|
|
|
+ - cos(2 * alpha) / sin(lattice_thetas[i]) * (cos(lattice_thetas[i]) + dalpha_dphi));
|
|
|
+ u_phi = normalization * (-cos(2 * alpha) * dalpha_dtheta
|
|
|
+ - sin(2 * alpha) / sin(lattice_thetas[i]) * (cos(lattice_thetas[i]) + dalpha_dphi));
|
|
|
+
|
|
|
+ velocity_norm = sqrt(u_theta * u_theta + u_phi * u_phi);
|
|
|
+
|
|
|
+ // velocity cutoff near defects
|
|
|
+ if (cutoff == 1 && velocity_norm > nu) {
|
|
|
+ u_theta = u_theta * nu / velocity_norm; // Renormalizing velocity near defects to exactly the defect speed
|
|
|
+ u_phi = u_phi * nu / velocity_norm;
|
|
|
+ }
|
|
|
+
|
|
|
+ // fixing the velocity field direction close to the defects
|
|
|
+ double psi_lattice;
|
|
|
+ if (direction_cutoff != 0) {
|
|
|
+ psi_lattice = atan2(u_phi, u_theta);
|
|
|
+
|
|
|
+ new_psi = psi_around_defect(lattice_thetas[i], lattice_phis[i], psi_lattice, defect_config[direction_cutoff - 1],
|
|
|
+ defect_config[4 + direction_cutoff - 1], defect_config[8 + direction_cutoff - 1], defect_dist);
|
|
|
+
|
|
|
+ u_theta = cos(new_psi) * nu;
|
|
|
+ u_phi = sin(new_psi) * nu;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ ephi(lattice_phis[i], vec_phi);
|
|
|
+ etheta(lattice_phis[i], lattice_thetas[i], vec_theta);
|
|
|
+
|
|
|
+ velocities[3*i] = u_theta * vec_theta[0] + u_phi * vec_phi[0];
|
|
|
+ velocities[3*i+1] = u_theta * vec_theta[1] + u_phi * vec_phi[1];
|
|
|
+ velocities[3*i+2] = u_theta * vec_theta[2] + u_phi * vec_phi[2];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double dot_product(double* vec1,double* vec2) {
|
|
|
+ return vec1[0] * vec2[0] + vec1[1] * vec2[1] +vec1[2] * vec2[2];
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void velocity_field_fast(double* defect_config, int n, double* lattice_thetas, double* lattice_phis,
|
|
|
+ double* lattice_basis, double nu, double rho, double* velocities, const int num_cores) {
|
|
|
+
|
|
|
+ // printf("Fast C function.\n");
|
|
|
+
|
|
|
+ double psi_bar[4];
|
|
|
+ psi_avg(&defect_config[0], &defect_config[4], defect_config[12], psi_bar);
|
|
|
+
|
|
|
+ double complex z_defect[4];
|
|
|
+ double delta_psi[4];
|
|
|
+ double defect_cart[12];
|
|
|
+
|
|
|
+ for (int i=0; i< 4; i++) {
|
|
|
+ z_defect[i] = stereogr_proj(defect_config[i], defect_config[4+i]);
|
|
|
+ delta_psi[i] = defect_config[8 + i] - psi_bar[i];
|
|
|
+ delta_psi[i] = delta_psi[i] - 2 * M_PI * floor((delta_psi[i] + M_PI) / (2 * M_PI));
|
|
|
+ sph_to_cart(defect_config[i], defect_config[4+i], &defect_cart[3*i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ double ln1_rho = log(1/rho);
|
|
|
+ double vel_cutoff = cos(1.25 * rho);
|
|
|
+ double dir_cutoff = cos(rho);
|
|
|
+
|
|
|
+ // double start = omp_get_wtime();
|
|
|
+
|
|
|
+ #pragma omp parallel for num_threads(num_cores) schedule(dynamic, 2500)
|
|
|
+ for (int i=0; i<n; i++) {
|
|
|
+ double complex sum_in_alpha_eq = 0;
|
|
|
+ double complex sum_in_dalpha_eq = 0;
|
|
|
+ double sum_in_alpha_tilde = 0;
|
|
|
+ int cutoff = 0;
|
|
|
+ int direction_cutoff = 0;
|
|
|
+ double defect_dist = 0; // distance from the closest defect
|
|
|
+
|
|
|
+ double dalpha_tilde_dphi = 0;
|
|
|
+ double dalpha_tilde_dtheta = 0;
|
|
|
+
|
|
|
+ double sin_lat_th = sin(lattice_thetas[i]);
|
|
|
+ double cos_lat_th = cos(lattice_thetas[i]);
|
|
|
+
|
|
|
+ double cos_beta_defect;
|
|
|
+ double sin_beta_defect;
|
|
|
+ double tan_beta_half;
|
|
|
+ double sin_beta_half;
|
|
|
+
|
|
|
+ double dbeta_dphi, dbeta_dtheta;
|
|
|
+
|
|
|
+ // numerical correction for the lattice point exactly at the north pole (needed for the stereographic projection).
|
|
|
+ if (lattice_thetas[i] < 1.0E-15) {
|
|
|
+ lattice_thetas[i] = 1.0E-15;
|
|
|
+ }
|
|
|
+
|
|
|
+ double complex z_point = stereogr_proj(lattice_thetas[i], lattice_phis[i]);
|
|
|
+
|
|
|
+ for (int j=0; j<4; j++) {
|
|
|
+
|
|
|
+ cos_beta_defect = dot_product(&lattice_basis[9*i], &defect_cart[3*j]);
|
|
|
+ if (cos_beta_defect > vel_cutoff) {
|
|
|
+ cutoff = 1; // possible velocity cutoff near the defects (fixed defect speed, diverges otherwise)
|
|
|
+ }
|
|
|
+ if (cos_beta_defect > dir_cutoff) {
|
|
|
+ defect_dist = acos(cos_beta_defect) / rho; // distance from the defect in units of rho
|
|
|
+ direction_cutoff = j + 1; // possible direction cutoff near the defects
|
|
|
+ }
|
|
|
+
|
|
|
+ sin_beta_defect = sqrt(1 - cos_beta_defect * cos_beta_defect);
|
|
|
+
|
|
|
+ // derivatives over theta and phi are equal to scalar products with other basis vectors
|
|
|
+ dbeta_dtheta = -dot_product(&lattice_basis[9*i + 3], &defect_cart[3*j]) / sin_beta_defect;
|
|
|
+ dbeta_dphi = -dot_product(&lattice_basis[9*i + 6], &defect_cart[3*j]) * sin_lat_th / sin_beta_defect;
|
|
|
+
|
|
|
+ sin_beta_half = sqrt((1 - cos_beta_defect) / 2);
|
|
|
+ tan_beta_half = (1 - cos_beta_defect) / sin_beta_defect;
|
|
|
+
|
|
|
+ sum_in_alpha_eq += clog(z_point - z_defect[j]);
|
|
|
+ sum_in_alpha_tilde += delta_psi[j] * (log(1 / sin_beta_half) - 1);
|
|
|
+ sum_in_dalpha_eq += z_point / (z_point - z_defect[j]);
|
|
|
+
|
|
|
+ dalpha_tilde_dphi += -delta_psi[j] / (4 * ln1_rho * tan_beta_half) * dbeta_dphi;
|
|
|
+ dalpha_tilde_dtheta += -delta_psi[j] / (4 * ln1_rho * tan_beta_half) * dbeta_dtheta;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ double alpha_eq = lattice_phis[i] - defect_config[12] - 0.5 * cimag(sum_in_alpha_eq);
|
|
|
+ double alpha_tilde = 1 / (2 * ln1_rho) * sum_in_alpha_tilde;
|
|
|
+ double alpha = alpha_eq + alpha_tilde;
|
|
|
+
|
|
|
+ double dalpha_eq_dphi = 1 - 0.5 * creal(sum_in_dalpha_eq);
|
|
|
+ double dalpha_eq_dtheta = 1 / (2 * sin_lat_th) * cimag(sum_in_dalpha_eq);
|
|
|
+
|
|
|
+ double dalpha_dphi = dalpha_eq_dphi + dalpha_tilde_dphi;
|
|
|
+ double dalpha_dtheta = dalpha_eq_dtheta + dalpha_tilde_dtheta;
|
|
|
+
|
|
|
+ // averaged delta psi for higher accuracy of renormalization
|
|
|
+ double avg_delta_psi = 0.25 * (fabs(delta_psi[0]) + fabs(delta_psi[1]) + fabs(delta_psi[2]) + fabs(delta_psi[3]));
|
|
|
+
|
|
|
+ double normalization = 2 * rho * nu / sqrt(1 + (avg_delta_psi/ln1_rho) * (avg_delta_psi/ln1_rho));
|
|
|
+
|
|
|
+ double u_theta = normalization * (sin(2 * alpha) * dalpha_dtheta
|
|
|
+ - cos(2 * alpha) / sin_lat_th * (cos_lat_th + dalpha_dphi));
|
|
|
+ double u_phi = normalization * (-cos(2 * alpha) * dalpha_dtheta
|
|
|
+ - sin(2 * alpha) / sin_lat_th * (cos_lat_th + dalpha_dphi));
|
|
|
+
|
|
|
+ double velocity_norm = sqrt(u_theta * u_theta + u_phi * u_phi);
|
|
|
+
|
|
|
+ // velocity cutoff near defects
|
|
|
+ if (cutoff == 1 && velocity_norm > nu) {
|
|
|
+ u_theta = u_theta * nu / velocity_norm; // Renormalizing velocity near defects to exactly the defect speed
|
|
|
+ u_phi = u_phi * nu / velocity_norm;
|
|
|
+ }
|
|
|
+
|
|
|
+ // fixing the velocity field direction close to the defects
|
|
|
+ double psi_lattice;
|
|
|
+ double new_psi; // for propagating flow direction near the defects
|
|
|
+ if (direction_cutoff != 0) {
|
|
|
+ psi_lattice = atan2(u_phi, u_theta);
|
|
|
+
|
|
|
+ new_psi = psi_around_defect(lattice_thetas[i], lattice_phis[i], psi_lattice, defect_config[direction_cutoff - 1],
|
|
|
+ defect_config[4 + direction_cutoff - 1], defect_config[8 + direction_cutoff - 1], defect_dist);
|
|
|
+
|
|
|
+ u_theta = cos(new_psi) * nu;
|
|
|
+ u_phi = sin(new_psi) * nu;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ velocities[3*i] = u_theta * lattice_basis[9*i + 3] + u_phi * lattice_basis[9*i + 6];
|
|
|
+ velocities[3*i+1] = u_theta * lattice_basis[9*i + 4] + u_phi * lattice_basis[9*i + 7];
|
|
|
+ velocities[3*i+2] = u_theta * lattice_basis[9*i + 5] + u_phi * lattice_basis[9*i + 8];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // double end = omp_get_wtime();
|
|
|
+ // printf("Loop time: %f\n",end - start);
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void velocity_field_drag(double* defect_config, int n, double* lattice_thetas, double* lattice_phis,
|
|
|
+ double* lattice_basis, double* lattice_grad_theta, double* lattice_grad_phi,
|
|
|
+ double nu, double mu, double rho, double* velocities, const int num_cores) {
|
|
|
+
|
|
|
+ // printf("Fast C function.\n");
|
|
|
+
|
|
|
+ double psi_bar[4];
|
|
|
+ psi_avg(&defect_config[0], &defect_config[4], defect_config[12], psi_bar);
|
|
|
+
|
|
|
+ double complex z_defect[4];
|
|
|
+ double delta_psi[4];
|
|
|
+ double defect_cart[12];
|
|
|
+
|
|
|
+ for (int i=0; i< 4; i++) {
|
|
|
+ z_defect[i] = stereogr_proj(defect_config[i], defect_config[4+i]);
|
|
|
+ delta_psi[i] = defect_config[8 + i] - psi_bar[i];
|
|
|
+ delta_psi[i] = delta_psi[i] - 2 * M_PI * floor((delta_psi[i] + M_PI) / (2 * M_PI));
|
|
|
+ sph_to_cart(defect_config[i], defect_config[4+i], &defect_cart[3*i]);
|
|
|
+ }
|
|
|
+
|
|
|
+ double ln1_rho = log(1/rho);
|
|
|
+ double vel_cutoff = cos(1.25 * rho);
|
|
|
+ double dir_cutoff = cos(rho);
|
|
|
+
|
|
|
+ // double start = omp_get_wtime();
|
|
|
+
|
|
|
+ #pragma omp parallel for num_threads(num_cores) schedule(dynamic, 2500)
|
|
|
+ for (int i=0; i<n; i++) {
|
|
|
+ double complex sum_in_alpha_eq = 0;
|
|
|
+ double complex sum_in_dalpha_eq = 0;
|
|
|
+ double sum_in_alpha_tilde = 0;
|
|
|
+ int cutoff = 0;
|
|
|
+ int direction_cutoff = 0;
|
|
|
+ double defect_dist = 0; // distance from the closest defect
|
|
|
+
|
|
|
+ double dalpha_tilde_dphi = 0;
|
|
|
+ double dalpha_tilde_dtheta = 0;
|
|
|
+
|
|
|
+ double sin_lat_th = sin(lattice_thetas[i]);
|
|
|
+ double cos_lat_th = cos(lattice_thetas[i]);
|
|
|
+
|
|
|
+ double cos_beta_defect;
|
|
|
+ double sin_beta_defect;
|
|
|
+ double tan_beta_half;
|
|
|
+ double sin_beta_half;
|
|
|
+
|
|
|
+ double dbeta_dphi, dbeta_dtheta;
|
|
|
+
|
|
|
+ // numerical correction for the lattice point exactly at the north pole (needed for the stereographic projection).
|
|
|
+ if (lattice_thetas[i] < 1.0E-15) {
|
|
|
+ lattice_thetas[i] = 1.0E-15;
|
|
|
+ }
|
|
|
+
|
|
|
+ double complex z_point = stereogr_proj(lattice_thetas[i], lattice_phis[i]);
|
|
|
+
|
|
|
+ for (int j=0; j<4; j++) {
|
|
|
+
|
|
|
+ cos_beta_defect = dot_product(&lattice_basis[9*i], &defect_cart[3*j]);
|
|
|
+ if (cos_beta_defect > vel_cutoff) {
|
|
|
+ cutoff = 1; // possible velocity cutoff near the defects (fixed defect speed, diverges otherwise)
|
|
|
+ }
|
|
|
+ if (cos_beta_defect > dir_cutoff) {
|
|
|
+ defect_dist = acos(cos_beta_defect) / rho; // distance from the defect in units of rho
|
|
|
+ direction_cutoff = j + 1; // possible direction cutoff near the defects
|
|
|
+ }
|
|
|
+
|
|
|
+ sin_beta_defect = sqrt(1 - cos_beta_defect * cos_beta_defect);
|
|
|
+
|
|
|
+ // derivatives over theta and phi are equal to scalar products with other basis vectors
|
|
|
+ dbeta_dtheta = -dot_product(&lattice_basis[9*i + 3], &defect_cart[3*j]) / sin_beta_defect;
|
|
|
+ dbeta_dphi = -dot_product(&lattice_basis[9*i + 6], &defect_cart[3*j]) * sin_lat_th / sin_beta_defect;
|
|
|
+
|
|
|
+ sin_beta_half = sqrt((1 - cos_beta_defect) / 2);
|
|
|
+ tan_beta_half = (1 - cos_beta_defect) / sin_beta_defect;
|
|
|
+
|
|
|
+ sum_in_alpha_eq += clog(z_point - z_defect[j]);
|
|
|
+ sum_in_alpha_tilde += delta_psi[j] * (log(1 / sin_beta_half) - 1);
|
|
|
+ sum_in_dalpha_eq += z_point / (z_point - z_defect[j]);
|
|
|
+
|
|
|
+ dalpha_tilde_dphi += -delta_psi[j] / (4 * ln1_rho * tan_beta_half) * dbeta_dphi;
|
|
|
+ dalpha_tilde_dtheta += -delta_psi[j] / (4 * ln1_rho * tan_beta_half) * dbeta_dtheta;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ double alpha_eq = lattice_phis[i] - defect_config[12] - 0.5 * cimag(sum_in_alpha_eq);
|
|
|
+ double alpha_tilde = 1 / (2 * ln1_rho) * sum_in_alpha_tilde;
|
|
|
+ double alpha = alpha_eq + alpha_tilde;
|
|
|
+
|
|
|
+ double dalpha_eq_dphi = 1 - 0.5 * creal(sum_in_dalpha_eq);
|
|
|
+ double dalpha_eq_dtheta = 1 / (2 * sin_lat_th) * cimag(sum_in_dalpha_eq);
|
|
|
+
|
|
|
+ double dalpha_dphi = dalpha_eq_dphi + dalpha_tilde_dphi;
|
|
|
+ double dalpha_dtheta = dalpha_eq_dtheta + dalpha_tilde_dtheta;
|
|
|
+
|
|
|
+ // averaged delta psi for higher accuracy of renormalization
|
|
|
+ double avg_delta_psi = 0.25 * (fabs(delta_psi[0]) + fabs(delta_psi[1]) + fabs(delta_psi[2]) + fabs(delta_psi[3]));
|
|
|
+
|
|
|
+ double normalization = 2 * rho * nu / sqrt(1 + (avg_delta_psi/ln1_rho) * (avg_delta_psi/ln1_rho));
|
|
|
+
|
|
|
+ double u_theta = normalization * (sin(2 * alpha) * dalpha_dtheta
|
|
|
+ - cos(2 * alpha) / sin_lat_th * (cos_lat_th + dalpha_dphi));
|
|
|
+ double u_phi = normalization * (-cos(2 * alpha) * dalpha_dtheta
|
|
|
+ - sin(2 * alpha) / sin_lat_th * (cos_lat_th + dalpha_dphi));
|
|
|
+
|
|
|
+ double velocity_norm = sqrt(u_theta * u_theta + u_phi * u_phi);
|
|
|
+
|
|
|
+ // velocity cutoff near defects
|
|
|
+ if (cutoff == 1 && velocity_norm > nu) {
|
|
|
+ u_theta = u_theta * nu / velocity_norm; // Renormalizing velocity near defects to exactly the defect speed
|
|
|
+ u_phi = u_phi * nu / velocity_norm;
|
|
|
+ }
|
|
|
+
|
|
|
+ // fixing the velocity field direction close to the defects
|
|
|
+ double psi_lattice;
|
|
|
+ double new_psi; // for propagating flow direction near the defects
|
|
|
+ if (direction_cutoff != 0) {
|
|
|
+ psi_lattice = atan2(u_phi, u_theta);
|
|
|
+
|
|
|
+ new_psi = psi_around_defect(lattice_thetas[i], lattice_phis[i], psi_lattice, defect_config[direction_cutoff - 1],
|
|
|
+ defect_config[4 + direction_cutoff - 1], defect_config[8 + direction_cutoff - 1], defect_dist);
|
|
|
+
|
|
|
+ u_theta = cos(new_psi) * nu;
|
|
|
+ u_phi = sin(new_psi) * nu;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ u_theta = u_theta - mu * lattice_grad_theta[i];
|
|
|
+ u_phi = u_phi - mu * lattice_grad_phi[i];
|
|
|
+
|
|
|
+ velocities[3*i] = u_theta * lattice_basis[9*i + 3] + u_phi * lattice_basis[9*i + 6];
|
|
|
+ velocities[3*i+1] = u_theta * lattice_basis[9*i + 4] + u_phi * lattice_basis[9*i + 7];
|
|
|
+ velocities[3*i+2] = u_theta * lattice_basis[9*i + 5] + u_phi * lattice_basis[9*i + 8];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // double end = omp_get_wtime();
|
|
|
+ // printf("Loop time: %f\n",end - start);
|
|
|
+
|
|
|
+
|
|
|
+}
|