diff --git a/hspm/api/core.py b/hspm/api/core.py index 53a19be82ed60bebdd056bcde83b406c1357411a..fd21cc302f6579ab00f3e817118e4ca84e027c12 100644 --- a/hspm/api/core.py +++ b/hspm/api/core.py @@ -1,6 +1,30 @@ +mesh_types = { + "CosN": 0, + "ConstantN": 1, + "Cos2N": 2, + "LinearS": 3, + "ExponentialS": 4 +} + def initHSPM(cfg): """ Initializes the HSPM object with the given configuration + + Parameters + ---------- + cfg : dict + Configuration dictionary + Contains: + - chord (float): Chord length + - aoa (float): Angle of attack + + * MESH PARAMETERS + - meshType (str): Type of mesh (CosN, ConstantN, Cos2N, LinearS, ExponentialS) + - sLe (float): Leading edge mesh size, only for S-type meshes + - sTe (float): Trailing edge mesh size, only for S-type meshes + - naca (str): NACA airfoil code + or + - filename (str): Airfoil file names """ import hspmw @@ -8,10 +32,28 @@ def initHSPM(cfg): hspm = hspmw.HSPM() - hspm.chord = cfg['chord'] # Not optimal, but it will do for now - hspm.V_inf = 1 # Shouldn't have any impact on the solution + hspm.chord = cfg['chord'] + hspm.V_inf = 1 + if not 'aoa' in cfg: + print("HSPM Warning: aoa not defined. Using default value of 0") + cfg['aoa'] = 0 hspm.AoA = np.deg2rad(cfg['aoa']) + if not "meshType" in cfg: + print("HSPM Warning: meshType not defined. Using default cosine mesh (CosN)") + cfg['meshType'] = "CosN" + hspm.meshType = mesh_types[cfg['meshType']] + if cfg['meshType'] == "LinearS" or cfg['meshType'] == "ExponentialS": + if not "sLe" in cfg: + print("HSPM Warning: Leading edge mesh size (sLe) not defined. Using default value of 0.01") + cfg['sLe'] = 0.01 + if not "sTe" in cfg: + print("HSPM Warning: Trailing edge mesh size (sTe) not defined. Using default value of 0.01") + cfg['sTe'] = 0.01 + hspm.sLe = cfg['sLe'] + hspm.sTe = cfg['sTe'] + cfg["N"] = 1 # Placeholder value for the generateNaca4DigitCoordinates function + if cfg['naca']: hspm.generateNaca4DigitCoordinates(int(cfg['naca'][0]), int(cfg['naca'][1]), int(cfg['naca'][2:4]), int(cfg['N'])) else: diff --git a/hspm/src/geometry.cpp b/hspm/src/geometry.cpp index efd5b934bfa0aea68fc85d40bd73c1350be1724c..6c1321e12a42af964317dde7aa49e6ba7de8b3ba 100644 --- a/hspm/src/geometry.cpp +++ b/hspm/src/geometry.cpp @@ -1,4 +1,5 @@ #include "geometry.h" +#include <iostream> void HSPM::generateNaca4DigitCoordinates(double camber, double camberPos, double thickness, size_t _N) { @@ -14,23 +15,106 @@ void HSPM::generateNaca4DigitCoordinates(double camber, double camberPos, double Thickness of the airfoil [%]. Third/fourth digits */ + if (meshType == LinearS) { + // Figure out how many elements are required + double N_double = 4.0/(sLe + sTe); + // Round to the nearest even number + _N = (size_t) ((int) N_double/2.0) * 2; + std::cout << "HSPM: The number of elements has been set to " << _N << " to match the prescribed sizes at the LE and TE." << std::endl; + } else if (meshType == ExponentialS) + { + // Figure out how many elements are required + if (sTe == sLe) { // In this case, we will use constant size elements (might be slightly off the prescribed size, but minimal variation is expected) + double N_double = 2.0 / sLe; + + // Round to the nearest even number + _N = (size_t) ((int) N_double/2.0) * 2; + std::cout << "HSPM: The number of elements has been set to " << _N << " to match the prescribed sizes at the LE and TE." << std::endl; + + sLe = 2.0 / _N; + sTe = 2.0 / _N; + + prog = 1.0; + } + else { + // Get the progression + double _r = sTe - sLe + 1; + double N_double = 2.0 * (log(sTe/sLe) / log(_r) + 1.0); + // Round to the nearest even number + _N = (size_t) ((int) N_double/2.0) * 2; + std::cout << "HSPM: The number of elements has been set to " << _N << " to match the prescribed sizes at the LE and TE." << std::endl; + + // Compute the value of the progression (needs to solve a nonlinear equation w/ Newton's method) + // 1-r = sLe * (1-r^(N/2)) + auto equation = [this](double r, double _N) { + return 1-r - sLe * (1-pow(r, _N/2.0)); + }; + + auto equationDerivative = [this](double r, double _N) { + return -1 - sLe * (-pow(r, _N/2.0-1) * _N/2.0); + }; + + double r0 = _r; + for (int i = 0; i < 1000; ++i) { + double h = equation(r0, _N) / equationDerivative(r0, _N); + if (std::abs(h) < 1e-8) { + break; + } + r0 = r0 - h; + } + prog = r0; + } + } + N = _N; x = Eigen::VectorXd(N+1); y = Eigen::VectorXd(N+1); Eigen::VectorXd angleDistribution = Eigen::VectorXd::LinSpaced(N+1, 0, 2*M_PI); - + Eigen::VectorXd x_distr = Eigen::VectorXd::Zero(N+1); for (size_t i=0; i<=N; i++) { double angle = angleDistribution(i); - /*if (angle < M_PI/2) { - x_distr(i) = chord/2 * (1 - angle * 2 / M_PI + 1); - } else if (angle > 3*M_PI/2) { - x_distr(i) = chord/2 * ((angle - 3*M_PI/2) * 2 / M_PI + 1); - } else { - x_distr(i) = chord/2 * (cos(angle)+1); - }*/ - x_distr(i) = chord * pow((1 + cos((angle + M_PI) / 2)), 2); + + if (meshType == CosN) // Cosine mesh with N elements + x_distr(i) = chord/2 * (1 + cos(angle)); + + else if (meshType == ConstantN) // Constant size mesh with N elements + { + if (angle < M_PI) + x_distr(i) = chord * (-M_1_PI * angle + 1); + else x_distr(i) = chord * (M_1_PI * angle - 1); + } + + else if (meshType == Cos2N) // Cosine square mesh with N elements + x_distr(i) = chord * pow((1 + cos((angle + M_PI) / 2)), 2); + + else if (meshType == LinearS) // Linear size mesh, with sizes at LE and TE (approx) prescribed + { // Maths au milieu de mon cahier + // Compute the size increment (could be saved to memory, but mesh generation is fast enough, so it's fine here) + double delta_s = (1.0 - (N/2.0)*sLe) / ((N/2.0*(N/2.0-1.0))/2.0); + if (i <= N/2.0) { // On the top side, from LE to TE + size_t j = N/2.0-i; + + x_distr(j) = sLe * i + delta_s * (i-1) * i / 2.0; + + } else { + x_distr(i) = x_distr(i-1) + sLe + delta_s * (i-(N/2.0+1)); + } + } + + else if (meshType == ExponentialS) // Exponential size mesh, with sizes @ LE and TE (approx) prescribed + { + if (i <= N/2.0) { // Top side + size_t j = N/2.0-i; + + if (prog == 1) x_distr(j) = sLe * i; + else x_distr(j) = sLe * (pow(prog, i) - 1) / (prog - 1); + + } else { // Bottom side + x_distr(i) = x_distr(i-1) + sLe * pow(prog, i-(N/2.0+1)); + } + } } for (size_t i=0; i<=N; i++) { @@ -97,6 +181,8 @@ void HSPM::loadCoordinates(char* fileName) { N = coordinates.cols() - 1; x = coordinates.row(0); y = coordinates.row(1); + + // TODO: will need to close some airfoils if the first and last points are not the same !!! } void HSPM::saveCoordinates(char* fileName) { diff --git a/hspm/src/hspm.h b/hspm/src/hspm.h index 640c1ba1aa8cab65c79c45c2ec8a4ce05bb08299..0e37ce4df2414947e8535b2de8ecf31e18cd5c8e 100644 --- a/hspm/src/hspm.h +++ b/hspm/src/hspm.h @@ -71,6 +71,19 @@ public: Eigen::VectorXd y_m_wake; Eigen::VectorXd U_wake; Eigen::VectorXd V_wake; + + // Mesh stuff + enum MeshType { + CosN = 0, + ConstantN = 1, + Cos2N = 2, + LinearS = 3, + ExponentialS = 4 + }; + MeshType meshType; + double sLe, sTe; + double prog; // For exponential mesh + private: Eigen::PartialPivLU<Eigen::MatrixXd> solver; };