Program Listing for File stress.h

Return to documentation for file (source/relations/stress.h)

/*
 * stress.h Atmospheric Boundary Layer Reynolds Stress relations
 *
 * Author:              Tom Clark  (thclark @ github)
 *
 * Copyright (c) 2019 Octue Ltd. All Rights Reserved.
 *
 */

#ifndef ES_FLOW_STRESS_H_
#define ES_FLOW_STRESS_H_

#include <Eigen/Dense>
#include <Eigen/Core>
#include <iostream>
#include <iomanip>
#include <math.h>
#include "NumericalIntegration.h"
#include "profile.h"
#include "utilities/trapz.h"
#include "utilities/cumtrapz.h"

using namespace utilities;


namespace es {


template<typename T>
T get_coles_wake(const T eta, const double pi_coles) {
    T wc;
    T eta_pow_2 = pow(eta, 2.0);
    wc = (2.0 * eta_pow_2 * (3.0 - (2.0 * eta)));
    wc -= (1.0 / pi_coles) * eta_pow_2 * (1.0 - eta) * (1.0 - (2.0 * eta));
    return wc;
}

// Remove template specialisation from doc (causes duplicate) @cond
Eigen::ArrayXd get_coles_wake(const Eigen::ArrayXd &eta, const double pi_coles) {
    Eigen::ArrayXd wc;
    Eigen::ArrayXd eta_pow_2;
    eta_pow_2 = eta.pow(2.0);
    wc = (2.0 * eta_pow_2 * (3.0 - (2.0 * eta)));
    wc -= (1.0 / pi_coles) * eta_pow_2 * (1.0 - eta) * (1.0 - (2.0 * eta));
    return wc;
}
// @endcond


Eigen::ArrayXd get_f_integrand(const Eigen::ArrayXd eta, const double kappa, const double pi_coles, const double shear_ratio) {

    Eigen::ArrayXd f;
    Eigen::ArrayXd ones;
    ones.setOnes(eta.size());
    f = (-1.0 / kappa) * eta.log()
        + (pi_coles/kappa) * get_coles_wake(ones, pi_coles) * ones
        - (pi_coles/kappa) * get_coles_wake(eta, pi_coles);
    for (int k = 0; k < f.size(); k++) {
        if (std::isinf(f[k])) {
            f(k) = shear_ratio; // from eqs 2 and 7 P&M part 1 1995
        }
    }

    return f;
}


void reynolds_stress_13(Eigen::ArrayXd &r13_a, Eigen::ArrayXd &r13_b, const double beta, const Eigen::ArrayXd &eta, const double kappa, const double pi_coles, const double shear_ratio, const double zeta){

    // Get f between eta = 0 and eta = 1 (bounds for C1 integration)
    Eigen::ArrayXd f = get_f_integrand(eta, kappa, pi_coles, shear_ratio);
    const double d_pi = 0.01 * pi_coles;
    Eigen::ArrayXd f_plus  = get_f_integrand(eta, kappa, (pi_coles + d_pi), shear_ratio);
    Eigen::ArrayXd f_minus  = get_f_integrand(eta, kappa, (pi_coles - d_pi), shear_ratio);

    // TODO can we cast this returned value directly?
    Eigen::ArrayXd c1_tmp;
    trapz(c1_tmp, eta, f);
    const double c1 = c1_tmp(0);

    // Do the integrations for ei with central differencing for numerical differentiation of e coefficients 5-7
    Eigen::ArrayXd e1;
    Eigen::ArrayXd e1_plus;
    Eigen::ArrayXd e1_minus;
    Eigen::ArrayXd e2;
    Eigen::ArrayXd e2_plus;
    Eigen::ArrayXd e2_minus;
    Eigen::ArrayXd e3;
    Eigen::ArrayXd e4;
    Eigen::ArrayXd e5;
    Eigen::ArrayXd e6;
    Eigen::ArrayXd e7;
    cumtrapz(e1, eta, f);
    cumtrapz(e1_plus, eta, f_plus);
    cumtrapz(e1_minus, eta, f_minus);
    cumtrapz(e2, eta, f.pow(2.0));
    cumtrapz(e2_plus, eta, f_plus.pow(2.0));
    cumtrapz(e2_minus, eta, f_minus.pow(2.0));
    e3 = f * e1;
    e4 = eta * f;
    e5 = (e1_plus - e1_minus) / (2.0 * d_pi);
    e6 = (e2_plus - e2_minus) / (2.0 * d_pi);
    e7 = f * e5;

    // Get A coefficients from equations A2 a-d
    Eigen::ArrayXd a1 = e2 - e3 + shear_ratio * (e4 - e1);
    Eigen::ArrayXd a2 = (-2.0 * e2) + e3 + (shear_ratio * e1);
    Eigen::ArrayXd a3 = e6 - e7 - (shear_ratio * e5);
    Eigen::ArrayXd a4 = (2.0 * e2) - e3 + (shear_ratio * e4) - (shear_ratio * 3.0 * e1);

    // B coefficients are simply evaluated at eta = 1 (eqn A6)
    // TODO eta must be defined up to 1! check this
    double b1 = a1(eta.rows()-1);
    double b2 = a2(eta.rows()-1);
    double b3 = a3(eta.rows()-1);
    double b4 = a4(eta.rows()-1);

    // E1 from (eqn A4). Can't call it E1 due to name conflict with above.
    double e1_coeff = 1.0 / (kappa * shear_ratio + 1.0);

    // N from (eqn A5) using central differencing as before
    double wc_minus = get_coles_wake(1.0, pi_coles - d_pi);
    double wc_plus =  get_coles_wake(1.0, pi_coles + d_pi);
    double n = get_coles_wake(1.0, pi_coles) + pi_coles * (wc_plus - wc_minus) / (2.0 * d_pi);

    // Compile f_i terms
    Eigen::ArrayXd f1;
    Eigen::ArrayXd f2;
    Eigen::ArrayXd f3;
    f1 = 1
        - a1 / (b1 + e1_coeff * b2)
        - e1_coeff * a2 / (b1 + e1_coeff * b2);

    f2 = (e1_coeff * n * a2 * b1
        + a3 * b1
        - e1_coeff * n * a1 * b2
        + e1_coeff * a3 * b2
        - a1 * b3
        - e1_coeff * a2 * b3) / (b1 + e1_coeff * b2);

    f3 = (e1_coeff * a2 * b1 + a4 * b1 - e1_coeff * a1 * b2 + e1_coeff * a4 * b2 - a1 * b4 - e1_coeff * a2 * b4) / (b1 + e1_coeff * b2);

    // Convert f2 and f3 into g1 and g2 ready for application of eq. 15
    Eigen::ArrayXd g1 = f2 / shear_ratio;
    Eigen::ArrayXd g2 = -f3 / (c1 * shear_ratio);

    // Top 3 boxes of figure 18
    Eigen::ArrayXd minus_r13 = f1 + g1 * zeta + g2 * beta;

    // Get the component due to equilibrium sink flow (OLD version - see P&M eqns 51,53)
    // minusReynStressA = ones(size(eta)) - eta + eta.*log(eta);

    // Lewkowicz 1982 shear stress for equilibrium sink flow, Perry and Marusic eqn. 51
    Eigen::ArrayXd minus_r13_a = 1.0 - (60.0/59.0)*eta - (20.0/59.0)*eta.pow(3.0) + (45.0/59.0)*eta.pow(4.0) - (24.0/59.0)*eta.pow(5.0) + (60.0/59.0)*eta*eta.log();

    // Handle the log(0) singularity
    for (int i = 0; i < minus_r13_a.size(); i++) {
        if (std::isinf(minus_r13_a(i))) {
            minus_r13_a(i) = 1.0;
        }
    }

    // Output correctly signed reynolds stress components
    r13_a = -1.0 * minus_r13_a;
    r13_b = -1.0 * (minus_r13 - minus_r13_a);

}

} /* namespace es */

#endif /* ES_FLOW_STRESS_H_ */