Пример #1
0
void llg_stt_rhs(double *dm_dt, double *m, double *h, double *h_stt,
                 double *alpha, double beta, double u0, double gamma, int n) {

    #pragma omp parallel for
    for (int index = 0; index < n; index++) {
        int i = 3 * index;
        int j = 3 * index + 1;
        int k = 3 * index + 2;

        double coeff = -gamma / (1 + alpha[index] * alpha[index]);

        double mm = m[i] * m[i] + m[j] * m[j] + m[k] * m[k];
        double mh = m[i] * h[i] + m[j] * h[j] + m[k] * h[k];

        //hp=mm.h-mh.m=-mx(mxh)
        double hpi = mm*h[i] - mh*m[i];
        double hpj = mm*h[j] - mh*m[j];
        double hpk = mm*h[k] - mh*m[k];

        double mth0 = cross_x(m[i], m[j], m[k], hpi, hpj, hpk);
        double mth1 = cross_y(m[i], m[j], m[k], hpi, hpj, hpk);
        double mth2 = cross_z(m[i], m[j], m[k], hpi, hpj, hpk);

        dm_dt[i] = coeff * (mth0 - hpi * alpha[index]);
        dm_dt[j] = coeff * (mth1 - hpj * alpha[index]);
        dm_dt[k] = coeff * (mth2 - hpk * alpha[index]);

        //the above part is standard LLG equation.

        double coeff_stt = u0 / (1 + alpha[index] * alpha[index]);

        double mht = m[i] * h_stt[i] + m[j] * h_stt[j] + m[k] * h_stt[k];

        hpi = mm*h_stt[i] - mht * m[i];
        hpj = mm*h_stt[j] - mht * m[j];
        hpk = mm*h_stt[k] - mht * m[k];

        mth0 = cross_x(m[i], m[j], m[k], hpi, hpj, hpk);
        mth1 = cross_y(m[i], m[j], m[k], hpi, hpj, hpk);
        mth2 = cross_z(m[i], m[j], m[k], hpi, hpj, hpk);

        dm_dt[i] += coeff_stt * ((1 + alpha[index] * beta) * hpi
                                 - (beta - alpha[index]) * mth0);
        dm_dt[j] += coeff_stt * ((1 + alpha[index] * beta) * hpj
                                 - (beta - alpha[index]) * mth1);
        dm_dt[k] += coeff_stt * ((1 + alpha[index] * beta) * hpk
                                 - (beta - alpha[index]) * mth2);

        double c = 6 * sqrt(dm_dt[i] * dm_dt[i] +
                            dm_dt[j] * dm_dt[j] +
                            dm_dt[k]* dm_dt[k]
                           );

        dm_dt[i] += c * (1 - mm) * m[i];
        dm_dt[j] += c * (1 - mm) * m[j];
        dm_dt[k] += c * (1 - mm) * m[k];

    }

}
Пример #2
0
void llg_rhs_baryakhtar(double *dm_dt, double *m, double *h, double *delta_h,
                        double *alpha, double beta, int *pins,
                        double gamma, int nxyz, int do_precession) {

    #pragma omp parallel for
    for (int id = 0; id < nxyz; id++) {
        int i = 3*id;
        int j = i + 1;
        int k = j + 1;

        if (pins[id]>0) {
            dm_dt[i] = 0;
            dm_dt[j] = 0;
            dm_dt[k] = 0;
            continue;
        }

        double coeff = -gamma;

        if (do_precession) {
            dm_dt[i] = coeff*cross_x(m[i],m[j],m[k],h[i],h[j],h[k]);
            dm_dt[j] = coeff*cross_y(m[i],m[j],m[k],h[i],h[j],h[k]);
            dm_dt[k] = coeff*cross_z(m[i],m[j],m[k],h[i],h[j],h[k]);
        }

        dm_dt[i] += gamma*(alpha[i]*h[i] - beta*delta_h[i]);
        dm_dt[j] += gamma*(alpha[i]*h[j] - beta*delta_h[j]);
        dm_dt[k] += gamma*(alpha[i]*h[k] - beta*delta_h[k]);

    }

}
Пример #3
0
void llg_rhs_baryakhtar_reduced(double *dm_dt, double *m, double *hp, double *delta_hp,
                                double *alpha, double beta, int *pins,
                                double gamma, int nxyz, int do_precession, double default_c) {

    #pragma omp parallel for
    for (int id = 0; id < nxyz; id++) {
        int i = 3*id;
        int j = i + 1;
        int k = j + 1;

        if (pins[id]>0) {
            dm_dt[i] = 0;
            dm_dt[j] = 0;
            dm_dt[k] = 0;
            continue;
        }

        double coeff = -gamma;

        if (do_precession) {
            dm_dt[i] = coeff*cross_x(m[i],m[j],m[k],hp[i],hp[j],hp[k]);
            dm_dt[j] = coeff*cross_y(m[i],m[j],m[k],hp[i],hp[j],hp[k]);
            dm_dt[k] = coeff*cross_z(m[i],m[j],m[k],hp[i],hp[j],hp[k]);
        }

        double hpx = alpha[i]*hp[i]-beta*delta_hp[i];
        double hpy = alpha[i]*hp[j]-beta*delta_hp[j];
        double hpz = alpha[i]*hp[k]-beta*delta_hp[k];

        double mm = m[i]*m[i] + m[j]*m[j] + m[k]*m[k];
        double mh = m[i]*hpx + m[j]*hpy + m[k]*hpz;

        //suppose m is normalised, i.e., hp=mm.h-mh.m=-mx(mxh)

        dm_dt[i] += gamma*(mm*hpx - mh*m[i]);
        dm_dt[j] += gamma*(mm*hpy - mh*m[j]);
        dm_dt[k] += gamma*(mm*hpz - mh*m[k]);

        double c=0;
        if (default_c<0) {
            c = 6*sqrt(dm_dt[i]*dm_dt[i]+dm_dt[j]*dm_dt[j]+dm_dt[k]*dm_dt[k]);
        } else {
            c = default_c;
        }
        //printf("%0.15g   %0.15g\n", c, default_c);

        dm_dt[i] += c*(1-mm)*m[i];
        dm_dt[j] += c*(1-mm)*m[j];
        dm_dt[k] += c*(1-mm)*m[k];

    }

}
Пример #4
0
void dmi_field_bulk(double *m, double *field, double *energy, double *Ms_inv,
                    double *D, double dx, double dy, double dz,
                    int n, int *ngbs) {

    /* In the atomic model, the effective field in the i-th spin site has the
     * summation: D_{ij} x S_{ij}
     *
     * where j is the index of a NN neighbour and D_{ij} = D * r_{ij}, r_{ij}
     * being the position vector connectin the site i with the site j
     *
     * In the continuum, the field can be written as : - nabla X M , which can
     * be discretised with an expression similar to the atomic model one when
     * doing the finite difference approximation of the derivative, thus we
     * only need the DMI vector as:  -D * r_{ij}
     *
     * So, in the loop through neighbours we compute:
     *
     * neighbour          field sum                this gives
     *   -x:      (D / dx) * (+x  X  M)   --> Components in y, z
     *   +x:      (D / dx) * (-x  X  M)   --> %
     *   -y:      (D / dy) * (+y  X  M)   --> Components in x, z
     *   +y:      (D / dy) * (-y  X  M)   --> %
     *   -z:      (D / dz) * (+z  X  M)   --> Components in x, y
     *   +z:      (D / dz) * (-z  X  M)   --> %
     *
     * which gives:
     *      field_x = (D / dx) * (mz[-x] - mz[+x])
     *                  + (D / dz) * (mx[-z] - mx[+x])
     *      ...
     *
     *  which are the first order derivatives.
     *
     *  To compute the DMI for other point groups we will have to find
     *  the vectors and how to discretise the derivative
     *
     */

    /* Here we iterate through every mesh node */
	#pragma omp parallel for
	for (int i = 0; i < n; i++) {
        double sign;
        double * dmivector = malloc(3 * sizeof(double));
        double DMIc;
	    double fx = 0, fy = 0, fz = 0;
	    int idnm = 0;     // Index for the magnetisation matrix
	    int idn = 6 * i; // index for the neighbours

        /* Set a zero field for sites without magnetic material */
	    if (Ms_inv[i] == 0.0){
	        field[3 * i] = 0;
	        field[3 * i + 1] = 0;
	        field[3 * i + 2] = 0;
            energy[i] = 0;
	        continue;
	    }

        /* Here we iterate through the neighbours. Remember:
         * j = 0, 1, 2, 3, 4, 5  --> -x, +x, -y, +y, -z, +z */
        for (int j = 0; j < 6; j++) {

            /* Remember that index=-1 is for sites without material, so
             * we skip those sites */
	        if (ngbs[idn + j] >= 0) {

                /* Since we are iterating through the 6 NN neighbours, we
                 * set the sign for the DMI vector since, for example,
                 * in the x directions, r_{ij} = (+-1, 0, 0)
                 * So, for j=0, 2, 4 (i.e. -x, -y, -z) we use a negatiev sign
                 */
                if (j == 0 || j == 2 || j == 4){ sign = -1; }
                else{ sign = 1; }

                /* Now we set the DMI vectors according to the neighbour position */
                if (j == 0 || j == 1) {
                    DMIc = -D[i] / dx;
                    dmivector[0] = sign, dmivector[1] = 0, dmivector[2] = 0;
                }
                else if (j == 2 || j == 3) {
                    DMIc = -D[i] / dy;
                    dmivector[0] = 0, dmivector[1] = sign, dmivector[2] = 0;
                }
                else if (j == 4 || j == 5) {
                    DMIc = -D[i] / dz;
                    dmivector[0] = 0, dmivector[1] = 0, dmivector[2] = sign;
                }

                /* Magnetisation array index of the neighbouring spin
                 * since ngbs gives the neighbour's index */
	            idnm = 3 * ngbs[idn + j];

                /* Check that the magnetisation of the neighbouring spin
                 * is larger than zero */
                if (Ms_inv[ngbs[idn + j]] > 0){

                    /* We do here:  (D / dx_i) * ( r_{ij} X M_{j} )
                     * The cross_i function gives the i component of the
                     * cross product
                     */

                    /* The x component of the cross product of +-x
                     * times anything is zero (similar for the other comps */
                    if (j != 0 && j != 1) {
                        fx += DMIc * cross_x(dmivector[0], dmivector[1], dmivector[2],
                                             m[idnm], m[idnm + 1], m[idnm + 2]);
                    }
                    if (j != 2 && j != 3) {
                        fy += DMIc * cross_y(dmivector[0], dmivector[1], dmivector[2],
                                             m[idnm], m[idnm + 1], m[idnm + 2]);
                    }
                    if (j != 4 && j != 5) {
                        fz += DMIc * cross_z(dmivector[0], dmivector[1], dmivector[2],
                                             m[idnm], m[idnm + 1], m[idnm + 2]);
                    }
                }
            }
        }

        /* Energy as: (-mu0 * Ms / 2) * [ H_dmi * m ]   */
        energy[i] = -0.5 * (fx * m[3 * i] + fy * m[3 * i + 1]
                            + fz * m[3 * i + 2]);

        /* Update the field H_dmi which has the same structure than *m */
        field[3 * i]     = fx * Ms_inv[i] * MU0_INV;
        field[3 * i + 1] = fy * Ms_inv[i] * MU0_INV;
        field[3 * i + 2] = fz * Ms_inv[i] * MU0_INV;

        free(dmivector);
    }
}
Пример #5
0
void dmi_field_interfacial(double *m, double *field, double *energy, double *Ms_inv,
                    double *D, double dx, double dy, double dz,
                    int n, int *ngbs) {

    /* In the atomic model, the effective field in the i-th spin site has the
     * summation: D_{ij} x S_{ij}
     *
     * For the Interfacial DMI, D_{ij} has the structure: D_{ij} = r_{ij} X z
     *
     * so the Dzyaloshinskii vectors are IN plane. This function only works
     * along the XY plane since we assume there is a non magnetic material below
     * with a different SOC which gives the DMI for neighbouring in plane spins
     *
     * The computation is similar than before, only that we no longer have
     * a z component. In the continuum the H_dmi field is:
     *          H_dmi = (2 * D / (Ms a a_z)) * ( x X dM/dy  - y X dM/dx )
     * where "a" is the lattice spacing in the plane and "a_z" the system
     * thickness along z
     * This derivative can be obtained doing the cross product
     * as in the atomic model, hence when going through the neighbours
     * loop, we can obtain the field doing the following cross products:
     *
     *   neighbour          field sum
     *     -x:      (D / dx) * (+y  X  M)
     *     +x:      (D / dx) * (-y  X  M)
     *     -y:      (D / dy) * (-x  X  M)
     *     +y       (D / dy) * (+x  X  M)
     * 
     * So, our Dzyaloshinskii vectors can be depicted in a square lattice as
     *
     *                     o  +y
     *                     |
     *                     --> D
     *                ^    | 
     *       -x  o __ | __ o __  | _ o  +x
     *                    |      v
     *                    <--
     *                    |
     *                    o  -y
     *
     * If we start with this picture in the atomic model, we can get the
     * continuum expression when doing the limit  a_x a_y a_z  --> 0
     *
     */

    /* Here we iterate through every mesh node */
	#pragma omp parallel for
	for (int i = 0; i < n; i++) {
        double sign;
        double DMIc;
        double * dmivector = malloc(3 * sizeof(double));
	    double fx = 0, fy = 0, fz = 0;
	    int idnm = 0;     // Index for the magnetisation matrix
	    int idn = 6 * i; // index for the neighbours

        /* Set a zero field for sites without magnetic material */
	    if (Ms_inv[i] == 0.0){
	        field[3 * i] = 0;
	        field[3 * i + 1] = 0;
	        field[3 * i + 2] = 0;
            energy[i] = 0;
	        continue;
	    }

        /* Here we iterate through the neighbours in the XY plane */
        for (int j = 0; j < 4; j++) {

            /* Remember that index=-1 is for sites without material */
	        if (ngbs[idn + j] >= 0) {
                
                /* Since we are iterating through 4 NN neighbours, we
                 * set the sign for the DMI vector since, for example,
                 * in the x directions, (r_{ij} X z) = (0, +-1, 0)
                 * So, for j=1, 2 (i.e. x, -y) we use a negatiev sign
                 * (see the DMI vector details in the above documentation)
                 */
                if (j == 1 || j == 2){ sign = -1; }
                else{ sign = 1; }

                /* Now we set the vectors with the mesh spacing below only
                 * for in plane neighbours */
                if (j == 0 || j == 1) {
                    DMIc = D[i] / dx;
                    dmivector[0] = 0, dmivector[1] = sign, dmivector[2] = 0;
                }
                else if (j == 2 || j == 3) {
                    DMIc = D[i] / dy;
                    dmivector[0] = sign, dmivector[1] = 0, dmivector[2] = 0;
                }

                /* Magnetisation array index of the neighbouring spin
                 * since ngbs gives the neighbour's index */
	            idnm = 3 * ngbs[idn + j];

                /* Check that the magnetisation of the neighbouring spin
                 * is larger than zero */
                if (Ms_inv[ngbs[idn + j]] > 0){

                    fx += DMIc * cross_x(dmivector[0], dmivector[1], dmivector[2],
                                         m[idnm], m[idnm + 1], m[idnm + 2]);
                    fy += DMIc * cross_y(dmivector[0], dmivector[1], dmivector[2],
                                         m[idnm], m[idnm + 1], m[idnm + 2]);
                    fz += DMIc * cross_z(dmivector[0], dmivector[1], dmivector[2],
                                         m[idnm], m[idnm + 1], m[idnm + 2]);
                }
            }
        }

        /* Energy as: (-mu0 * Ms / 2) * [ H_dmi * m ]   */
        energy[i] = -0.5 * (fx * m[3 * i] + fy * m[3 * i + 1]
                            + fz * m[3 * i + 2]);

        /* Update the field H_dmi which has the same structure than *m */
        field[3 * i]     = fx * Ms_inv[i] * MU0_INV;
        field[3 * i + 1] = fy * Ms_inv[i] * MU0_INV;
        field[3 * i + 2] = fz * Ms_inv[i] * MU0_INV;

        free(dmivector);
    }
}
Пример #6
0
void dmi_field_bulk(double *m, double *field, double *energy, double *Ms_inv,
                    double *D, double dx, double dy, double dz,
                    int n, int *ngbs) {

    /* In the atomic model, the effective field in the i-th spin site has the
     * summation: D_{ij} x S_{ij}
     *
     * where j is the index of a NN neighbour and D_{ij} = D * r_{ij}, r_{ij}
     * being the position vector connectin the site i with the site j
     *
     * In the continuum, the field can be written as : - nabla X M , which can
     * be discretised with an expression similar to the atomic model one when
     * doing the finite difference approximation of the derivative, thus we
     * only need the DMI vector as:  -D * r_{ij}
     *
     * So, in the loop through neighbours we compute:
     *
     * neighbour          field sum                this gives
     *   -x:      (D / dx) * (+x  X  M)   --> Components in y, z
     *   +x:      (D / dx) * (-x  X  M)   --> %
     *   -y:      (D / dy) * (+y  X  M)   --> Components in x, z
     *   +y:      (D / dy) * (-y  X  M)   --> %
     *   -z:      (D / dz) * (+z  X  M)   --> Components in x, y
     *   +z:      (D / dz) * (-z  X  M)   --> %
     *
     * which gives:
     *      field_x = (D / dx) * (mz[-x] - mz[+x])
     *                  + (D / dz) * (mx[-z] - mx[+x])
     *      ...
     *
     *  which are the first order derivatives.
     *
     *  The DMI vector norms are given by the *D array. If our simulation has
     *  n mesh nodes, then the D array is (6 * n) long, i.e. the DMI vector norm
     *  per every neighbour per every mesh node. The order is the same than the NNs
     *  array, i.e. 
     *
     *      D = [D(x)_0, D(-x)_0, D(y)_0, D(-y)_0, D(z)_0, D(-z)_0, D(x)_1, ...]
     *
     *  where D(j)_i means the DMI vector norm of the NN in the j-direction at
     *  the i-th mesh node. Remember that the DMI vector points in a single
     *  direction towards the NN site, e.g. the DMI vector of the NN in the
     *  +y direction for the 0th spin, is DMI_vector = D(y)_0 * (0, 1, 0)
     *
     *  NOTE:
     *  To compute the DMI for other point groups we will have to find
     *  the vectors and how to discretise the derivative
     *
     */

    /* The DMI vector directions are the same according to the neighbours
     * positions. Thus we set them here to avoid compute them
     * every time in the loop . So, if we have the j-th NN,
     * the DMI vector will be dmivector[3 * j] */
    double dmivector[18] = {-1,  0,  0,
                             1,  0,  0,
                             0, -1,  0,
                             0,  1,  0,
                             0,  0, -1,
                             0,  0,  1
                             };

    /* These are for the DMI prefactor or coefficient */
    double dxs[6] = {dx, dx, dy, dy, dz, dz};

    /* Here we iterate through every mesh node */
	#pragma omp parallel for shared(dmivector, dxs)
	for (int i = 0; i < n; i++) {
        double DMIc;
	    double fx = 0, fy = 0, fz = 0;
	    int idnm = 0;     // Index for the magnetisation matrix
	    int idn = 6 * i; // index for the neighbours

        /* Set a zero field for sites without magnetic material */
	    if (Ms_inv[i] == 0.0){
	        field[3 * i] = 0;
	        field[3 * i + 1] = 0;
	        field[3 * i + 2] = 0;
            energy[i] = 0;
	        continue;
	    }

        /* Here we iterate through the neighbours. Remember:
         * j = 0, 1, 2, 3, 4, 5  --> -x, +x, -y, +y, -z, +z */
        for (int j = 0; j < 6; j++) {

            /* Remember that index=-1 is for sites without material, so
             * we skip those sites */
	        if (ngbs[idn + j] >= 0) {

                /* Magnetisation array index of the neighbouring spin
                 * since ngbs gives the neighbour's index */
	            idnm = 3 * ngbs[idn + j];

                /* Check that the magnetisation of the neighbouring spin
                 * is larger than zero */
                if (Ms_inv[ngbs[idn + j]] > 0){

                    /* We do here:  (D / dx_i) * ( r_{ij} X M_{j} )
                     * The cross_i function gives the i component of the
                     * cross product. The coefficient is computed according
                     * to the DMI strength of the current lattice site.
                     * For the denominator, for example, if j=2 or 3, then
                     * dxs[j] = dy
                     * The D vector is 6 * n which is specified for every neighbour
                     */
                    DMIc = -D[idn + j] / dxs[j];

                    /* Compute only for DMI vectors largr than zero */
                    if (abs(DMIc) > 0) {
                        /* The x component of the cross product of +-x
                         * times anything is zero (similar for the other comps) */
                        if (j != 0 && j != 1) {
                            fx += DMIc * cross_x(dmivector[3 * j],
                                                 dmivector[3 * j + 1],
                                                 dmivector[3 * j + 2],
                                                 m[idnm], m[idnm + 1], m[idnm + 2]);
                        }
                        if (j != 2 && j != 3) {
                            fy += DMIc * cross_y(dmivector[3 * j],
                                                 dmivector[3 * j + 1],
                                                 dmivector[3 * j + 2],
                                                 m[idnm], m[idnm + 1], m[idnm + 2]);
                        }
                        if (j != 4 && j != 5) {
                            fz += DMIc * cross_z(dmivector[3 * j],
                                                 dmivector[3 * j + 1],
                                                 dmivector[3 * j + 2],
                                                 m[idnm], m[idnm + 1], m[idnm + 2]);
                        }
                    }
                }
            }
        }

        /* Energy as: (-mu0 * Ms / 2) * [ H_dmi * m ]   */
        energy[i] = -0.5 * (fx * m[3 * i] + fy * m[3 * i + 1]
                            + fz * m[3 * i + 2]);

        /* Update the field H_dmi which has the same structure than *m */
        field[3 * i]     = fx * Ms_inv[i] * MU0_INV;
        field[3 * i + 1] = fy * Ms_inv[i] * MU0_INV;
        field[3 * i + 2] = fz * Ms_inv[i] * MU0_INV;

    }
}
Пример #7
0
void dmi_field_interfacial(double *m, double *field, double *energy, double *Ms_inv,
                    double *D, double dx, double dy, double dz,
                    int n, int *ngbs) {

    /* In the atomic model, the effective field in the i-th spin site has the
     * summation: D_{ij} x S_{ij}
     *
     * For the Interfacial DMI, D_{ij} has the structure: D_{ij} = r_{ij} X z
     * See Rohart et al. Phys. Rev. B 88, 184422)
     *
     * but [Yang et al. Phys. Rev. Lett. 115, 267210] uses the opposite sign (?)
     *
     * so the Dzyaloshinskii vectors are IN plane. This function only works
     * along the XY plane since we assume there is a non magnetic material below
     * with a different SOC which gives the DMI for neighbouring in plane spins
     *
     * The computation is similar than before, only that we no longer have
     * a z component. In the continuum the H_dmi field is:
     *          H_dmi = (2 * D / (Ms a a_z)) * ( x X dM/dy  - y X dM/dx )
     * where "a" is the lattice spacing in the plane and "a_z" the system
     * thickness along z
     * This derivative can be obtained doing the cross product
     * as in the atomic model, hence when going through the neighbours
     * loop, we can obtain the field doing the following cross products:
     *
     *   neighbour          field sum
     *     -x:      (D / dx) * (+y  X  M)
     *     +x:      (D / dx) * (-y  X  M)
     *     -y:      (D / dy) * (-x  X  M)
     *     +y       (D / dy) * (+x  X  M)
     *
     * So, our Dzyaloshinskii vectors can be depicted in a square lattice as
     *
     *                     o  +y
     *                     |
     *                    --> D
     *                ^    |     
     *       -x  o __ | __ o __  | _ o  +x
     *                     |     v
     *                    <--
     *                     |
     *                     o  -y
     *
     * If we start with this picture in the atomic model, we can get the
     * continuum expression when doing the limit  a_x a_y a_z  --> 0
     *
     *  The DMI vector norms are given by the *D array. If our simulation has
     *  n mesh nodes, then the D array is (4 * n) long, i.e. the DMI vector norm
     *  per every neighbour per every mesh node. The order is the same than the NNs
     *  array, i.e. 
     *
     *      D = [D(x)_0, D(-x)_0, D(y)_0, D(-y)_0, D(x)_1, ...]
     *
     *  where D(j)_i means the DMI vector norm of the NN in the j-direction at
     *  the i-th mesh node. Remember that the DMI vector points in a single
     *  direction towards the NN site, e.g. the DMI vector of the NN in the
     *  +y direction for the 0th spin, is DMI_vector = D(y)_0 * (0, 1, 0)
     */

    /* We set the DMi vectors here. For the j-th NN, the DMI
     * vector starts at dmivector[3 * j]
     * (NNs are in the order: -x, +x, -y, +y)
     * For interfacial DMI we only compute the DMI in 2D
     * */
    double dmivector[12] = { 0,  1,  0,
                             0, -1,  0,
                            -1,  0,  0,
                             1,  0,  0,
                             };
    double dxs[4] = {dx, dx, dy, dy};

    /* Here we iterate through every mesh node */
	#pragma omp parallel for shared(dmivector, dxs)
	for (int i = 0; i < n; i++) {
        double sign;
        double DMIc;
	    double fx = 0, fy = 0, fz = 0;
	    int idnm = 0;     // Index for the magnetisation matrix
	    int idn = 6 * i; // index for the neighbours
	    int idn_DMI = 4 * i; // index for the neighbours for the DMI array

        /* Set a zero field for sites without magnetic material */
	    if (Ms_inv[i] == 0.0){
	        field[3 * i] = 0;
	        field[3 * i + 1] = 0;
	        field[3 * i + 2] = 0;
            energy[i] = 0;
	        continue;
	    }

        /* Here we iterate through the neighbours in the XY plane */
        for (int j = 0; j < 4; j++) {

            /* Remember that index=-1 is for sites without material */
	        if (ngbs[idn + j] >= 0) {

                /* DMI coefficient according to the neighbour position */
                DMIc = D[idn_DMI + j] / dxs[j];

                /* Magnetisation array index of the neighbouring spin
                 * since ngbs gives the neighbour's index */
	            idnm = 3 * ngbs[idn + j];

                /* Check that the magnetisation of the neighbouring spin
                 * is larger than zero, as well as the DMI coefficient */
                if (Ms_inv[ngbs[idn + j]] > 0 && abs(DMIc) > 0) {

                    fx += DMIc * cross_x(dmivector[3 * j],
                                         dmivector[3 * j + 1],
                                         dmivector[3 * j + 2],
                                         m[idnm], m[idnm + 1], m[idnm + 2]);
                    fy += DMIc * cross_y(dmivector[3 * j],
                                         dmivector[3 * j + 1],
                                         dmivector[3 * j + 2],
                                         m[idnm], m[idnm + 1], m[idnm + 2]);
                    fz += DMIc * cross_z(dmivector[3 * j],
                                         dmivector[3 * j + 1],
                                         dmivector[3 * j + 2],
                                         m[idnm], m[idnm + 1], m[idnm + 2]);
                }
            }
        }

        /* Energy as: (-mu0 * Ms / 2) * [ H_dmi * m ]   */
        energy[i] = -0.5 * (fx * m[3 * i] + fy * m[3 * i + 1]
                            + fz * m[3 * i + 2]);

        /* Update the field H_dmi which has the same structure than *m */
        field[3 * i]     = fx * Ms_inv[i] * MU0_INV;
        field[3 * i + 1] = fy * Ms_inv[i] * MU0_INV;
        field[3 * i + 2] = fz * Ms_inv[i] * MU0_INV;

    }
}
Пример #8
0
void dmi_field_bulk(double *spin, double *field,
                    double *energy, double D, int *ngbs, int nxyz) {

    /* Bulk DMI field and energy computation
     *
     * ngbs[] contains the *indexes* of the neighbours
     * in the following order:
     *      -x, +x, -y, +y, -z, +z
     *
     * for every spin. It is -1 for boundaries.
     * The array is like:
     *      | 0-x, 0+x, 0-y, 0+y, 0-z, 0+z, 1-x, 1+x, 1-y, ...  |
     *        i=0                           i=1                ...
     *
     * where  0-y  is the index of the neighbour of the 0th spin,
     * in the -y direction, for example
     *
     * Thus, for every neighbour ( ngbs[i + j], j=0,1,...5 )
     * we compute the field contribution.
     *
     * The value of ngbs[] at sites with Ms = 0, is negative
     *
     * The ngbs array also gives the correct indexes for the spins
     * at periodic boundaries
     *
     * The Bulk DMI Dzyaloshinskii vectors are in the same direction than the
     * r_ij vectors which connect two neighbouring sites, pointing from the
     * lattice site *i* towards *j*
     *
     * The bulk DMI is defined in 3 dimensions
     *
     * Then the DMI field is computed as :
     *
     *      Sum_j ( D_ij X S_j ) = Sum_j ( r_ij X S_j )
     *
     * for every spin *i*
     *
     * --- Check this: can we use a SC lattice for this bulk DMI expression?
     *     Since, for example, MnSi crystal structure is more complex 
     */

	#pragma omp parallel for
	for (int i = 0; i < nxyz; i++) {

		int id = 0;
		int idv = 6 * i; // index for the neighbours

		double fx = 0, fy = 0, fz = 0;

		if (ngbs[idv]>=0) { // neighbour at x-1
			id = 3*ngbs[idv];
			fx += D*cross_x(-1,0,0,spin[id],spin[id+1],spin[id+2]);
			fy += D*cross_y(-1,0,0,spin[id],spin[id+1],spin[id+2]);
			fz += D*cross_z(-1,0,0,spin[id],spin[id+1],spin[id+2]);
		}

		if (ngbs[idv+1]>=0) { // neighbour x+1
			id = 3*ngbs[idv+1];
			fx += D*cross_x(1,0,0,spin[id],spin[id+1],spin[id+2]);
			fy += D*cross_y(1,0,0,spin[id],spin[id+1],spin[id+2]);
			fz += D*cross_z(1,0,0,spin[id],spin[id+1],spin[id+2]);
		}

		if (ngbs[idv+2]>=0) { // neighbour at y-1
			id = 3*ngbs[idv+2];
			fx += D*cross_x(0,-1,0,spin[id],spin[id+1],spin[id+2]);
			fy += D*cross_y(0,-1,0,spin[id],spin[id+1],spin[id+2]);
			fz += D*cross_z(0,-1,0,spin[id],spin[id+1],spin[id+2]);
		}

		if (ngbs[idv+3]>=0) { // neighbour at y+1
			id = 3*ngbs[idv+3];
			fx += D*cross_x(0,1,0,spin[id],spin[id+1],spin[id+2]);
			fy += D*cross_y(0,1,0,spin[id],spin[id+1],spin[id+2]);
			fz += D*cross_z(0,1,0,spin[id],spin[id+1],spin[id+2]);
		}

		if (ngbs[idv+4]>=0) { // neighbour at z-1
			id = 3*ngbs[idv+4];
			fx += D*cross_x(0,0,-1,spin[id],spin[id+1],spin[id+2]);
			fy += D*cross_y(0,0,-1,spin[id],spin[id+1],spin[id+2]);
			fz += D*cross_z(0,0,-1,spin[id],spin[id+1],spin[id+2]);
		}

		if (ngbs[idv+5]>=0) { // neighbour at z+1
			id = 3*ngbs[idv+5];
			fx += D*cross_x(0,0,1,spin[id],spin[id+1],spin[id+2]);
			fy += D*cross_y(0,0,1,spin[id],spin[id+1],spin[id+2]);
			fz += D*cross_z(0,0,1,spin[id],spin[id+1],spin[id+2]);
		}

		field[3 * i] = fx;
        field[3 * i + 1] = fy;
        field[3 * i + 2] = fz;

        energy[i] = -0.5 * (fx * spin[3 * i] +
                            fy * spin[3 * i + 1] +
                            fz * spin[3 * i + 2]
                            );
    }
}
Пример #9
0
void dmi_field_interfacial_atomistic(double *spin, double *field, double *energy,
    double D, int *ngbs, int n, int nneighbours, double *DMI_vec) {
    
    /* Interfacial DMI field and energy computation
     *
     * ngbs[] contains the *indexes* of the neighbours
     * for different lattices. The number of neighbours is specified with the
     * nneighbours integer
     *
     * For example, in a simple cubic (SC) lattice,
     * the array is like:
     *      | 0-x, 0+x, 0-y, 0+y, 0-z, 0+z, 1-x, 1+x, 1-y, ...  |
     *        i=0                           i=1                ...
     *
     * where  0-y  is the index of the neighbour of the 0th spin,
     * in the -y direction, for example
     *
     * Thus, for every neighbour of the i-th spin ( ngbs[i + j], j=0,1,...5 )
     * we compute the field contribution
     * The value of ngbs[] at sites with Ms = 0, is negative (-1)
     *
     * The ngbs array also gives the correct indexes for the spins
     * at periodic boundaries
     *
     * The Interfacial DMI needs the direction of the Dzyaloshinskii vectors,
     * which are obtained as
     *
     *      D_ij = r_ij  X  +z
     *
     * where r_ij is the vector connecting two neighbouring sites.
     * This vectors are computed in Python and passed here as the DMI_vec array,
     * whose entries are according to the neighbours matrix order, i.e.
     * the (DMI_vec[3 * j], DMI_vec[3 * j + 1], DMI_vec[3 * j + 2]) vector
     * are the Dij vector components between the i-th site and the j-th neighbour
     * (we assume the vectors are the same between NNs for every lattice site)
     *
     * 
     * For instance, in a SC 2D lattice, the X -th spin has the DMI vectors:
     * [ lattice site X,  neighbours O ]
     *
     *                    O
     *                    .
     *                   --->  D_ij
     *                    .
     *              ^     .                  ^ y
     *          O . | . . X . . | . O        |
     *                    .     v            |---> x
     *                    .
     *                   <---
     *                    .
     *                    O
     *
     * so DMI_vec = {0, 1, 0, 0, -1, 0, -1, 0, 0, 1, 0, 0}
     * NN:          j=0      j=1        j=2      j=3
     *
     * This DMI is only defined in two dimensions--> interfaces, thin films
     * Then the DMI field is computed as :  Sum_j ( D_ij X S_j )
     * for every spin *i*
     *
     * In a hexagonal lattice we have 6 neighbours
     *
     * We assume a constant DMI vector magnitude
     */
    
  	#pragma omp parallel for
	for (int i = 0; i < n; i++) {
        
		int idn = 6 * i; // index for the NNs
		int idnm = 0;    // index for the magnetisation components of the NNs

		double fx = 0, fy = 0, fz = 0;

        /* Now we compute the field contribution from every neighbour */
        for(int j = 0; j < nneighbours; j++){
            /* Check that Ms != 0 */
            if (ngbs[idn + j] >= 0) {

                /* Now we add the field contribution of the j-th
                 * neighbour for the i-th spin: D_ij X S_j */
                idnm = 3 * ngbs[idn + j];  // Magnetisation component index of the j-th NN
                fx += D * cross_x(DMI_vec[3 * j], DMI_vec[3 * j + 1], DMI_vec[3 * j + 2],
                                  spin[idnm], spin[idnm + 1], spin[idnm + 2]);
                fy += D * cross_y(DMI_vec[3 * j], DMI_vec[3 * j + 1], DMI_vec[3 * j + 2],
                                  spin[idnm], spin[idnm + 1], spin[idnm + 2]);
                fz += D * cross_z(DMI_vec[3 * j], DMI_vec[3 * j + 1], DMI_vec[3 * j + 2],
                                  spin[idnm], spin[idnm + 1], spin[idnm + 2]);
            }
        }

        field[3 * i] = fx;
  	    field[3 * i + 1] = fy;
  	    field[3 * i + 2] = fz;

        // TODO: check whether the energy is correct or not.
        /* Avoid second counting with 1/2 */
  	    energy[i] = -0.5 * (fx * spin[3 * i] +
                            fy * spin[3 * i + 1]+
                            fz * spin[3 * i + 2]
                            );
    }
}