package com.ruoyi.caltools.service;


import com.ruoyi.caltools.model.GasProps;
import com.ruoyi.caltools.utils.GasConstants;
import org.springframework.stereotype.Service;

@Service
public class ThermService {
    private static final double CAL_TH = 4.1840;
    public int coefA = 0;
    public int coefB = 1;
    public int coefC = 2;
    public int coefD = 3;
    public int coefE = 4;
    public int coefF = 5;
    public int coefG = 6;
    public int coefH = 7;
    public int coefI = 8;
    public int coefJ = 9;
    public int coefK = 10;

    double dPdD;// partial deriv of P wrt D
    double dPdT;// partial deriv of P wrt T
    double dSi;// ideal gas specific entropy, kJ/kg.K
    double dTold;// temperature previously used
    double dMrxold;// mixture molar mass previously used
       //set the number of points for quadrature
    int GK_points = 5;
    // 初始化 GK_root 数组
    double[] GK_root = { 0.14887433898163121088, 0.43339539412924719080, 0.67940956829902440263, 0.86506336668898451073, 0.97390652851717172008 };
    // 初始化 GK_weight 数组
    double[] GK_weight = { 0.29552422471475286217, 0.26926671930999634918, 0.21908636251598204295, 0.14945134915058059038, 0.066671344308688137179 };

    private final double[][] ThermConstants = {
            {-29776.4, 7.95454, 43.9417, 1037.09, 1.56373, 813.205, -24.9027, 1019.98, -10.1601, 1070.14, -20.0615},
            {-3495.34, 6.95587, 0.272892, 662.738, -0.291318, -680.562, 1.78980, 1740.06, 0.0, 100.0, 4.49823},
            {20.7307, 6.96237, 2.68645, 500.371, -2.56429, -530.443, 3.91921, 500.198, 2.13290, 2197.22, 5.81381},
            {-37524.4, 7.98139, 24.3668, 752.320, 3.53990, 272.846, 8.44724, 1020.13, -13.2732, 869.510, -22.4010},
            {-56072.1, 8.14319, 37.0629, 735.402, 9.38159, 247.190, 13.4556, 1454.78, -11.7342, 984.518, -24.0426},
            {-13773.1, 7.97183, 6.27078, 2572.63, 2.05010, 1156.72, 0.0, 100.0, 0.0, 100.0, -3.24989},
            {-10085.4, 7.94680, -0.08380, 433.801, 2.85539, 843.792, 6.31595, 1481.43, -2.88457, 1102.23, -0.51551},
            {-5565.60, 6.66789, 2.33458, 2584.98, 0.749019, 559.656, 0.0, 100.0, 0.0, 100.0, -7.94821},
            {-2753.49, 6.95854, 2.02441, 1541.22, 0.096774, 3674.81, 0.0, 100.0, 0.0, 100.0, 6.23387},
            {-3497.45, 6.96302, 2.40013, 2522.05, 2.21752, 1154.15, 0.0, 100.0, 0.0, 100.0, 9.19749},
            {-72387.0, 17.8143, 58.2062, 1787.39, 40.7621, 808.645, 0.0, 100.0, 0.0, 100.0, -44.1341},
            {-72674.8, 18.6383, 57.4178, 1792.73, 38.6599, 814.151, 0.0, 100.0, 0.0, 100.0, -46.1938},
            {-91505.5, 21.3861, 74.3410, 1701.58, 47.0587, 775.899, 0.0, 100.0, 0.0, 100.0, -60.2474},
            {-83845.2, 22.5012, 69.5789, 1719.58, 46.2164, 802.174, 0.0, 100.0, 0.0, 100.0, -62.2197},
            {-94982.5, 26.6225, 80.3819, 1718.49, 55.6598, 802.069, 0.0, 100.0, 0.0, 100.0, -77.5366},
            {-103353.0, 30.4029, 90.6941, 1669.32, 63.2028, 786.001, 0.0, 100.0, 0.0, 100.0, -92.0164},
            {-109674.0, 34.0847, 100.253, 1611.55, 69.7675, 768.847, 0.0, 100.0, 0.0, 100.0, -106.149},
            {-122599.0, 38.5014, 111.446, 1646.48, 80.5015, 781.588, 0.0, 100.0, 0.0, 100.0, -122.444},
            {-133564.0, 42.7143, 122.173, 1654.85, 90.2255, 785.564, 0.0, 100.0, 0.0, 100.0, -138.006},
            {0.0, 4.9680, 0.0, 100.0, 0.0, 100.0, 0.0, 100.0, 0.0, 100.0, 0.0},
            {0.0, 4.9680, 0.0, 100.0, 0.0, 100.0, 0.0, 100.0, 0.0, 100.0, 0.0}
    };
    public ThermService()
    {
        // initialize 3 history-sensitive variables
        dSi = 0.0;
        dTold = 0.0;
        dMrxold = 0.0;
    }

    public void Run(GasProps gasProps, com.ruoyi.caltools.service.DetailService  detailService)
    {
        //local variables
        double c, x, y, z;
        //first run basic set of functions within AGA 8 (1994) Detail Method 
        detailService.run(gasProps);
        //find first partial derivative of Z wrt D
        detailService.dZdD(gasProps.dDf);
        //find real gas cv, cp, specific enthalpy and entropy
        CprCvrHS(gasProps, detailService);
        //ratio of real gas specific heats
        gasProps.dk = gasProps.dCp / gasProps.dCv;
        //solve c in three steps, for clarity and ease of debugging 
        x = gasProps.dk * GasConstants.RGAS * 1000.0 * gasProps.dTf;
        y = gasProps.dMrx;
        z = gasProps.dZf + gasProps.dDf * detailService.ddZdD;
        //calculate c, which is SOS^2
        c = (x / y) * z;
        //speed of sound 
        gasProps.dSOS = Math.sqrt(c);
        //calculate the real gas isentropic exponent
        //using expression functionally equivalent to Equation 3.2
        gasProps.dKappa = (c * gasProps.dRhof) / gasProps.dPf;
        return;
    }
    private double CpiMolar(GasProps gasProps) {
        double cp = 0.0;
        double Cpx;
        double DT, FT, HT, JT;
        double Dx, Fx, Hx, Jx;
        double T;
        T = gasProps.dTf;
        for (int i = 0; i < GasConstants.NUMBEROFCOMPONENTS; i++) {
            if (gasProps.getAdMixture()[i] > 0) {
                // 计算每个组分的贡献
                Cpx = 0.0;
                // calculate species intermediate terms
                DT = ThermConstants[i][ coefD] / T;
                FT = ThermConstants[i][ coefF] / T;
                HT = ThermConstants[i][ coefH] / T;
                JT = ThermConstants[i][ coefJ] / T;
                // use intermediate terms to avoid redundant calcs
                Dx = DT / Math.sinh(DT);
                Fx = FT / Math.cosh(FT);
                Hx = HT / Math.sinh(HT);
                Jx = JT / Math.cosh(JT);
                Cpx += ThermConstants[i][ coefB];
                Cpx += ThermConstants[i][ coefC] * Dx * Dx;
                Cpx += ThermConstants[i][ coefE] * Fx * Fx;
                Cpx += ThermConstants[i][ coefG] * Hx * Hx;
                Cpx += ThermConstants[i][ coefI] * Jx * Jx;
                //use current mole fraction to weight the contribution
                Cpx *= gasProps.adMixture[i];
                //add this contribution to the sum
                cp += Cpx;
            }
        }
        return cp * CAL_TH;
    }
    // coth 函数实现
    private static double coth(double x) {
        return 1.0 / Math.tanh(x);
    }
    // Ho 函数
    public double Ho(GasProps gasProps) {
        double H = 0.0;
        double Hx;
        double DT, FT, HT, JT;
        double cothDT, tanhFT, cothHT, tanhJT;
        double T = gasProps.dTf;

        for (int i = 0; i < GasConstants.NUMBEROFCOMPONENTS; i++) {
            if (gasProps.adMixture[i] <= 0.0) continue;
            Hx = 0.0;

            DT = ThermConstants[i][coefD] / T;
            FT = ThermConstants[i][coefF] / T;
            HT = ThermConstants[i][coefH] / T;
            JT = ThermConstants[i][coefJ] / T;

            cothDT = coth(DT);
            tanhFT = Math.tanh(FT);
            cothHT = coth(HT);
            tanhJT = Math.tanh(JT);

            Hx += ThermConstants[i][coefA];
            Hx += ThermConstants[i][coefB] * T;
            Hx += ThermConstants[i][coefC] * ThermConstants[i][coefD] * cothDT;
            Hx -= ThermConstants[i][coefE] * ThermConstants[i][coefF] * tanhFT;
            Hx += ThermConstants[i][coefG] * ThermConstants[i][coefH] * cothHT;
            Hx -= ThermConstants[i][coefI] * ThermConstants[i][coefJ] * tanhJT;

            Hx *= gasProps.adMixture[i];
            H += Hx;
        }

        H *= CAL_TH;
        H /= gasProps.dMrx;
        return H * 1000.0;
    }

    // So 函数
    public double So(GasProps gasProps) {
        double S = 0.0;
        double Sx;
        double DT, FT, HT, JT;
        double cothDT, tanhFT, cothHT, tanhJT;
        double sinhDT, coshFT, sinhHT, coshJT;
        double T = gasProps.dTf;

        for (int i = 0; i < GasConstants.NUMBEROFCOMPONENTS; i++) {
            if (gasProps.adMixture[i] <= 0.0) continue;
            Sx = 0.0;

            DT = ThermConstants[i][coefD] / T;
            FT = ThermConstants[i][coefF] / T;
            HT = ThermConstants[i][coefH] / T;
            JT = ThermConstants[i][coefJ] / T;

            cothDT = coth(DT);
            tanhFT = Math.tanh(FT);
            cothHT = coth(HT);
            tanhJT = Math.tanh(JT);

            sinhDT = Math.sinh(DT);
            coshFT = Math.cosh(FT);
            sinhHT = Math.sinh(HT);
            coshJT = Math.cosh(JT);

            Sx += ThermConstants[i][coefK];
            Sx += ThermConstants[i][coefB] * Math.log(T);
            Sx += ThermConstants[i][coefC] * (DT * cothDT - Math.log(sinhDT));
            Sx -= ThermConstants[i][coefE] * (FT * tanhFT - Math.log(coshFT));
            Sx += ThermConstants[i][coefG] * (HT * cothHT - Math.log(sinhHT));
            Sx -= ThermConstants[i][coefI] * (JT * tanhJT - Math.log(coshJT));

            Sx *= gasProps.adMixture[i];
            S += Sx;
        }

        S *= CAL_TH;
        S /= gasProps.dMrx;
        return S * 1000.0;
    }

    // CprCvrHS 函数
    public void CprCvrHS(GasProps gasProps, com.ruoyi.caltools.service.DetailService detailService) {
        double Cvinc = 0.0;
        double Cvr, Cpr;
        double Hinc = 0.0;
        double Sinc = 0.0;
        double Smixing = 0.0;
        double Cp = CpiMolar(gasProps);
        double Si;

        gasProps.dHo = Ho(gasProps);
        Si = So(gasProps);

        gasProps.dCpi = (Cp * 1000.0) / gasProps.dMrx;

        for (int i = 0; i < GK_points; i++) {
            double x = gasProps.dDf * (1.0 + GK_root[i]) / 2.0;
            detailService.zdetail(x);
            detailService.dZdT(x);
            detailService.d2ZdT2(x);

            Hinc += GK_weight[i] * detailService.ddZdT / x;
            Cvinc += GK_weight[i] * (2.0 * detailService.ddZdT + gasProps.dTf * detailService.dd2ZdT2) / x;
            Sinc += GK_weight[i] * (detailService.dZ + gasProps.dTf * detailService.ddZdT - 1.0) / x;

            x = gasProps.dDf * (1.0 - GK_root[i]) / 2.0;
            detailService.zdetail(x);
            detailService.dZdT(x);
            detailService.d2ZdT2(x);

            Hinc += GK_weight[i] * detailService.ddZdT / x;
            Cvinc += GK_weight[i] * (2.0 * detailService.ddZdT + gasProps.dTf * detailService.dd2ZdT2) / x;
            Sinc += GK_weight[i] * (detailService.dZ + gasProps.dTf * detailService.ddZdT - 1.0) / x;
        }

        detailService.zdetail(gasProps.dDf);
        detailService.dZdT(gasProps.dDf);
        detailService.d2ZdT2(gasProps.dDf);

        Cvr = Cp - GasConstants.RGAS * (1.0 + gasProps.dTf * Cvinc * 0.5 * gasProps.dDf);

        double a = (gasProps.dZf + gasProps.dTf * detailService.ddZdT);
        double b = (gasProps.dZf + gasProps.dDf * detailService.ddZdD);

        double dPdT =GasConstants. RGAS * gasProps.dDf * a;
        double dPdD = GasConstants.RGAS * gasProps.dTf * b;

        Cpr = Cvr + GasConstants.RGAS * ((a * a) / b);

        Cpr /= gasProps.dMrx;
        Cvr /= gasProps.dMrx;

        gasProps.dCv = Cvr * 1000.0;
        gasProps.dCp = Cpr * 1000.0;

        gasProps.dH = gasProps.dHo + 1000.0 * GasConstants.RGAS * gasProps.dTf * (gasProps.dZf - 1.0 - gasProps.dTf * Hinc * 0.5 * gasProps.dDf) / gasProps.dMrx;

        for (int i = 0; i < GasConstants.NUMBEROFCOMPONENTS; i++) {
            if (gasProps.adMixture[i] != 0) Smixing += gasProps.adMixture[i] * Math.log(gasProps.adMixture[i]);
        }
        Smixing *= GasConstants.RGAS;

        gasProps.dS = Si - Smixing - 1000.0 * GasConstants.RGAS * (Math.log(gasProps.dPf / 101325.0) - Math.log(gasProps.dZf) + Sinc * 0.5 * gasProps.dDf) / gasProps.dMrx;
    }

    // HS_Mode 函数
    public void HS_Mode( GasProps gasProps, DetailService detailService, double H, double S, boolean bGuess) {
        double s0 = S;
        double h0 = H;
        double t1, t2, tmin, tmax;
        double p1, p2, px, pmin, pmax;
        double delta1, delta2;
        double tolerance = 0.001;
        if (bGuess) {
            t1 = gasProps.dTf;
            px = gasProps.dPf;
            pmax = px * 2.0;
            pmin = px * 0.1;
            tmax = t1 * 1.5;
            tmin = t1 * 0.67;
        } else {
            t1 = 273.15;
            px = 1013250.0;
            pmax = GasConstants.P_MAX;
            pmin = 10000.0;
            tmax = GasConstants.T_MAX;
            tmin = GasConstants.T_MIN;
        }

        t2 = t1 + 10.0;

        detailService.run(gasProps);
        double h1 = H(gasProps, detailService) - h0;

        for (int i = 0; i < GasConstants.MAX_NUM_OF_ITERATIONS; i++) {
            gasProps.dTf = t2;
            p1 = px;
            p2 = px * 0.1;
            gasProps.dPf = p1;
            detailService.run(gasProps);
            double s1 = S(gasProps, detailService) - s0;

            for (int j = 0; j < GasConstants.MAX_NUM_OF_ITERATIONS; j++) {
                gasProps.dPf = p2;
                detailService.run(gasProps);
                double s2 = S(gasProps, detailService) - s0;

                delta2 = Math.abs(s1 - s2) / s0;
                if (delta2 < tolerance) break;

                double p0 = p2;
                p2 = (p1 * s2 - p2 * s1) / (s2 - s1);

                if (p2 <= pmin) p2 = pmin;
                if (p2 >= pmax) p2 = pmax;

                p1 = p0;
                s1 = s2;
            }

            if (gasProps.lStatus == GasConstants.MAX_NUM_OF_ITERATIONS_EXCEEDED) break;

            double h2 = H(gasProps, detailService) - h0;
            delta1 = Math.abs(h1 - h2) / h0;

            if (delta1 < tolerance && i > 0) break;

            double t0 = t2;
            t2 = (t1 * h2 - t2 * h1) / (h2 - h1);

            if (t2 >= tmax) t2 = tmax;
            if (t2 <= tmin) {
                t2 = t0 + 10.0;
                gasProps.dTf = t2;
                detailService.run(gasProps);
                h2 = H(gasProps, detailService) - h0;
            }

            t1 = t0;
            h1 = h2;
        }

        if (gasProps.lStatus == GasConstants.MAX_NUM_OF_ITERATIONS_EXCEEDED) {
            gasProps.lStatus = GasConstants.MAX_NUM_OF_ITERATIONS_EXCEEDED;
        }
    }

    // H 函数
    public double H(GasProps gasProps, DetailService detailService) {
        double Hinc = 0.0;
        gasProps.dHo = Ho(gasProps);

        for (int i = 0; i < GK_points; i++) {
            double x = gasProps.dDf * (1.0 + GK_root[i]) / 2.0;
            detailService.zdetail(x);
            detailService.dZdT(x);
            detailService.d2ZdT2(x);

            Hinc += GK_weight[i] * detailService.ddZdT / x;
            if (i == 10) break;

            x = gasProps.dDf * (1.0 - GK_root[i]) / 2.0;
            detailService.zdetail(x);
            detailService.dZdT(x);
            detailService.d2ZdT2(x);

            Hinc += GK_weight[i] * detailService.ddZdT / x;
        }

        detailService.zdetail(gasProps.dDf);
        detailService.dZdT(gasProps.dDf);
        detailService.d2ZdT2(gasProps.dDf);

        gasProps.dH = gasProps.dHo + 1000.0 * GasConstants.RGAS * gasProps.dTf * (gasProps.dZf - 1.0 - gasProps.dTf * Hinc * 0.5 * gasProps.dDf) / gasProps.dMrx;
        return gasProps.dH;
    }



    double S(GasProps gasProps,  DetailService detailService)
    {
        double Sinc; double Smixing; double x;
        int i;
        //initialize integral 
        Sinc = 0.0;
        //initialize entropy of mixing 
        Smixing = 0.0;
        //integrate partial derivatives from D=0 to D=D, applying Gauss-Kronrod quadrature
        for (i = 0; i <GK_points ; i++)
        {
            //calculate 1st and 2nd partial derivatives of Z wrt T
            x = gasProps.dDf * (1.0 + GK_root[i]) / 2.0; detailService.zdetail(x);
            detailService.dZdT(x); detailService.d2ZdT2(x);
            Sinc += GK_weight[i] * (detailService.dZ + gasProps.dTf * detailService.ddZdT - 1.0) / x;
            if (i == 10) break;
            x = gasProps.dDf * (1.0 - GK_root[i]) / 2.0; detailService.zdetail(x);
            detailService.dZdT(x); detailService.d2ZdT2(x);
            Sinc += GK_weight[i] * (detailService.dZ + gasProps.dTf * detailService.ddZdT - 1.0) / x;
        }
        //reset Z and partial deivatives dZdT and d2ZdT2
        detailService.zdetail(gasProps.dDf);
        detailService.dZdT(gasProps.dDf);
        detailService.d2ZdT2(gasProps.dDf);
        //find ideal gas entropy, but only if temperature or composition have changed
        if (gasProps.dTf != dTold || gasProps.dMrx != dMrxold)
        {
            dSi = So(gasProps); dTold = gasProps.dTf; dMrxold = gasProps.dMrx;
        }
        //calculate entropy of mixing
        for (i = 0; i < GasConstants.NUMBEROFCOMPONENTS; i++)
        {
            if (gasProps.adMixture[i] != 0) Smixing += gasProps.adMixture[i] * Math.log(gasProps.adMixture[i]);
        }
        Smixing *= GasConstants.RGAS;
        // calculate specific entropy
        gasProps.dS = dSi - Smixing - 1000.0 * GasConstants.RGAS * (Math.log(gasProps.dPf / 101325.0) - Math.log(gasProps.dZf) + Sinc * 0.5 * gasProps.dDf) / gasProps.dMrx;
        return (gasProps.dS);
    }


}