最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Facing problem with re-renders in react (or anything else?) - Stack Overflow

programmeradmin4浏览0评论

I am working on creating a mccabe thiele plotter (a chemical engineering topic) which will take some parameters as input and show a plot (using chartjs)

For this, I need to select one among two options 'vol' or 'datapts'. In vol mode (relative volatility mode), equilibrium data equi will be calculated from a equation and for datapts mode (equilibrium data), i need to provide equi data:

For toggling selection and automatically updating equi data:

useEffect(() => {
        if (vol === 'datapts') addEqui([{x: 0, y: 0}, {x: 1, y: 1}])
        else if (vol === 'vol' && alpha > 1) equi_rel_from_alpha();
    }, [vol, alpha]);
function equi_rel_from_alpha() {
        try {
            let newEqui = []
            let x
            let y_eq
            for (let i = round(0); round(i) <= 1; i += 0.1) {
                x = round(i)
                y_eq = (alpha * x) / (1 + (alpha - 1) * x)
                newEqui.push({ x: x, y: Number(y_eq.toFixed(4)) })
            }
            addEqui(newEqui)
        } catch (err) {
            setErr('Error in finding equilibrium curve from alpha')
        }
    }
 function round(el) {
        return Math.round(el * 100) / 100
    }

Azeo is a random function as to calculate how many times the equi data lines will cross y=x line:

    const [azeo, setAzeo] = useState([])    
    
    function check_azeo(){
    
        const xy = {a: 1, b: -1, c: 0}
        const len = equi.length
        let azeo_temp = []
        let pos = 1
        if(len>2){
            let p1
            let p2
            let ln
            while(pos+1!=len-1){
                p1 = equi[pos]
                p2 = equi[pos+1]
                if((xy.a*p1.x + xy.b*p1.y + xy.c) == 0){
                    azeo_temp.push(p1.x)
                }
                else if((xy.a*p1.x + xy.b*p1.y + xy.c)*(xy.a*p2.x + xy.b*p2.y + xy.c)<0){
                    ln = line_from_2points(p1, p2)
                    azeo_temp.push(intersection_of_2lines(xy, ln).x)
                }
                pos += 1
            }   
        }
        setAzeo(azeo_temp)
    }    

Now once I have selected the mode, there will be parameters provided (D, F, etc.) and mccabe_thiele function will be called based on criteria and mode as below:

useEffect(() => {
        if (D && F && xD && xF && q && op_ratio && q && vol && alpha && equi.length > 1) {
            if (D > 0 && F > 0 && F > D && xF < 1 && xD < 1 && xF > 0 && xD > 0 && alpha > 1 && F * xF > D * xD && xD > xF && op_ratio > 1) {
                if (vol === 'vol') {
                    setErr('')
                    mcCabe_thiele()
                }
                else if (vol === 'datapts') {
                    
                    if(azeo.length > 0){
                        setErr('Cant plot for azeotropes')
                    }
                    else if (equi.length >= 5) {
                        setErr('')
                        mcCabe_thiele()
                    }
                    else setErr('Atleast 5 equilibrium points must be provided')
                }
            }
            else if (alpha <= 1) setErr('Relative volatility must be greater than 1')
            else if (D <= 0) setErr('Distillate rate must be greater than 0')
            else if (F <= 0) setErr('Feed rate must be greater than 0')
            else if (F < D) setErr('Feed rate must be greater than distillate rate')
            else if (F * xF < D * xD) setErr('You are expecting more than you are feeding')
            else if (xD <= xF) setErr('Distillate purity must be more than feed purity')
            else if (op_ratio <= 1) setErr('Optimum ratio must be greater than 1')
            else if (xD <= 0) setErr('Distillate purity cannot be zero')
            else if (xD >= 1) setErr('Distillate cannot be completely pure')
            else if (xF <= 0) setErr('Feed cannot be single component')
            else if (xF >= 1) setErr('Feed cannot be single component')
        }
    }, [alpha, D, F, xD, xF, q, op_ratio, equi, azeo])

Here, D, F, xD, xF, q, opt_ratio are parameters and need not be worried about.

I am facing a very very weird issue:

  1. When I am using vol mode, its working completely fine but mccabe thiele is being called twice.

  2. When I am switching to datapts mode and entering valid equi data (5 points), its working fine but again mccabe thiele is called twice.

  3. In case when I am intentionally using points in datapts mode where azeo is non-empty array, weirdly still mccabe_thiele function is being called (which shouldnt be the case when azeo.length>0) and stages are being drawn according to mccabe thiele method (but using equi data of vol mode). So system is using vol mode equi data for calculation when azeo is non-empty, I want the plot to not execute the mccabe thiele function in this case.

I know the problem is too long, but I want the solution. I am not using any strict mode, please help!!! (all parameter inputs are working fine)

McCabe_math.jsx

import { useEffect } from "react"

export default function McCabe_math({ D, F, xF, xD, q, vol, alpha, op_ratio, setW, setxW, addEqui, setR, setErr, equi, setSteps, setfdInter, setRm, setStages, azeo, setAzeo }) {

    function round(el) {
        return Math.round(el * 100) / 100
    }

    useEffect(() => {
        setErr('')
    }, [vol, D, F, xD, xF, q, op_ratio])

    useEffect(() => {
        if (vol === 'datapts') addEqui([{x: 0, y: 0}, {x: 1, y: 1}])
        else if (vol === 'vol' && alpha > 1) equi_rel_from_alpha();
    }, [vol, alpha]);


    // useEffect(() => {
    //     if (azeo.length > 0) setErr('Azeotrope formation')
    // }, [azeo])


    useEffect(() => {
        if (D && F && xD && xF && q && op_ratio && q && vol && alpha && equi.length > 1) {
            if (D > 0 && F > 0 && F > D && xF < 1 && xD < 1 && xF > 0 && xD > 0 && alpha > 1 && F * xF > D * xD && xD > xF && op_ratio > 1) {
                if (vol === 'vol') {
                    setErr('')
                    mcCabe_thiele()
                }
                else if (vol === 'datapts') {
                    
                    if(azeo.length > 0){
                        setErr('Cant plot for azeotropes')
                        setStages([])
                    }
                    else if (equi.length >= 5) {
                        setErr('')
                        mcCabe_thiele()
                    }
                    else setErr('Atleast 5 equilibrium points must be provided')
                }
            }
            else if (alpha <= 1) setErr('Relative volatility must be greater than 1')
            else if (D <= 0) setErr('Distillate rate must be greater than 0')
            else if (F <= 0) setErr('Feed rate must be greater than 0')
            else if (F < D) setErr('Feed rate must be greater than distillate rate')
            else if (F * xF < D * xD) setErr('You are expecting more than you are feeding')
            else if (xD <= xF) setErr('Distillate purity must be more than feed purity')
            else if (op_ratio <= 1) setErr('Optimum ratio must be greater than 1')
            else if (xD <= 0) setErr('Distillate purity cannot be zero')
            else if (xD >= 1) setErr('Distillate cannot be completely pure')
            else if (xF <= 0) setErr('Feed cannot be single component')
            else if (xF >= 1) setErr('Feed cannot be single component')
        }
    }, [alpha, D, F, xD, xF, q, op_ratio, equi, azeo])

    function equi_rel_from_alpha() {
        try {
            let newEqui = []
            let x
            let y_eq
            for (let i = round(0); round(i) <= 1; i += 0.1) {
                x = round(i)
                y_eq = (alpha * x) / (1 + (alpha - 1) * x)
                newEqui.push({ x: x, y: Number(y_eq.toFixed(4)) })
            }
            addEqui(newEqui)
        } catch (err) {
            setErr('Error in finding equilibrium curve from alpha')
        }
    }

    function line_from_2points(p1, p2) {
        let x1 = p1.x
        let y1 = p1.y
        let x2 = p2.x
        let y2 = p2.y
        let m = (y2 - y1) / (x2 - x1)
        return { a: m, b: -1, c: y1 - m * x1 }
    }

    function closestPoint(a, b, c) {
        let init = Math.abs(c / (Math.pow(a * a + b * b, 0.5)))
        let min = { val: init, pt: { x: 0, y: 0 } }

        for (let el of equi) {
            let xi = el.x
            let yi = el.y
            let dist = Math.abs((a * xi + b * yi + c) / (Math.pow(a * a + b * b, 0.5)))
            if (dist == 0) return { x: xi, y: yi, ptr: 'exact', dist: 0 }  //when the intersection is an actual data point
            else if (dist < min.val) {
                min.val = dist
                min.pt = { x: xi, y: yi }
            }
        }
        return { x: min.pt.x, y: min.pt.y, ptr: 'non-exact', dist: min.val }
    }

    function sideofPoint(xi, yi, a, b, c) {
        let s_origin = a * 0 + b * 0 + c
        let s_point = a * xi + b * yi + c
        if (s_origin * s_point < 0) return 'right'
        else if (s_origin * s_point > 0) return 'left'
    }

    function complementaryPoint(xi, side) {
        const index = equi.findIndex(el => el.x === xi)
        if (side === 'right') return equi[index - 1]
        else if (side === 'left') return equi[index + 1]
    }

    function intersection_of_2lines(l1, l2) {
        let a1 = l1.a
        let b1 = l1.b
        let c1 = l1.c
        let a2 = l2.a
        let b2 = l2.b
        let c2 = l2.c

        return { x: (b1 * c2 - b2 * c1) / (a1 * b2 - a2 * b1), y: (a2 * c1 - a1 * c2) / (a1 * b2 - a2 * b1) }
    }

    function q_line() {
        try {
            if (Number(q) === 1) return { a: 1, b: 0, c: Number(-xF) }
            else {
                let feed_slope = (-1) * (q / (1 - q))
                return { a: (-1) * feed_slope, b: 1, c: xF * (feed_slope - 1) }
            }
        } catch (err) {
            setErr('Error in finding q line')
        }
    }

    function feed_intersection() {
        try {
            let feed_slope
            let closestPt
            let side
            let compPt
            if (Number(q) === 1) {
                //feed line eqn: x + 0*y - xF = 0
                closestPt = closestPoint(1, 0, -1 * (xF))
                if (closestPt.ptr === 'exact') return { x: closestPt.x, y: closestPt.y }
                else {
                    side = sideofPoint(closestPt.x, closestPt.y, 1, 0, -1 * (xF))
                }
            }
            else {
                //feed line eqn: (-feed_slope)x + y + xF(feed_slope - 1) = 0
                feed_slope = (-1) * (q / (1 - q))
                closestPt = closestPoint((-1) * feed_slope, 1, xF * (feed_slope - 1), equi)
                if (closestPt.ptr === 'exact') return { x: closestPt.x, y: closestPt.y }
                else {
                    side = sideofPoint(closestPt.x, closestPt.y, (-1) * feed_slope, 1, xF * (feed_slope - 1))
                }
            }
            compPt = complementaryPoint(closestPt.x, side)
            let comp_line = line_from_2points(closestPt, compPt)

            if (Number(q) === 1) return intersection_of_2lines(comp_line, { a: 1, b: 0, c: (-1) * Number(xF) })
            else return intersection_of_2lines(comp_line, { a: (-1) * feed_slope, b: 1, c: xF * (feed_slope - 1) })
        } catch (err) {
            setErr('Error in finding feed intersection with curve')
        }
    }

    function step_intersection(yi) {
        let snap = -1;

        for (let i = 0; i < equi.length; i++) {
            if (equi[i].y === yi) return { x: equi[i].x, y: equi[i].y }; // Exact match
            if (equi[i].y > yi) {
                snap = i;
                break; // Stop at the first point where y > yi
            }
        }

        // If `yi` is outside the range, return null
        if (snap === -1 || snap === 0) return null; // No valid intersection

        let p1 = equi[snap - 1];
        let p2 = equi[snap];

        let l1 = line_from_2points(p1, p2);
        let l2 = { a: 0, b: 1, c: -yi };

        return intersection_of_2lines(l1, l2);
    }

    function mcCabe_thiele() {

        console.log('mct called')

        //top_composition
        let top_compo = { x: xD, y: xD }

        //to find out the intersection point of feed line with equilibrium curve after finding closest and complementary eq. data points and interpolation
        const feed_equi = feed_intersection()

        //q-line
        let q_ln = q_line()

        //pinch calculations
        let pinch_slope = (feed_equi.y - top_compo.y) / (feed_equi.x - top_compo.x)

        let Rmin = pinch_slope / (1 - pinch_slope)
        setRm(Rmin)
        let Ropt = Rmin * op_ratio
        setR(Ropt)

        let rect_slope = Ropt / (Ropt + 1)
        let rect = { a: (-1) * rect_slope, b: 1, c: xD * (rect_slope - 1) }

        //intersection of feed and rectifying line
        let feed_rect_intersection = intersection_of_2lines(q_ln, rect)

        try {
            setfdInter(feed_rect_intersection)
        } catch (err) {
            setErr('Error in finding feed and rectification line intersection')
        }

        //stripping line
        let W = F - D
        setW(W)
        let xW = (F * xF - D * xD) / W
        setxW(xW)
        let bottom_compo = { x: xW, y: xW }
        let strip = line_from_2points(feed_rect_intersection, bottom_compo)

        //steps calculation
        let step_pts = []
        let x_curr = xD
        let y_curr
        let stage = 'rect'
        let check
        let next

        try {
            while (true) {
                //rectifying area
                if (stage === 'rect') {
                    //horizontal step
                    y_curr = (-1) * (rect.a * x_curr + rect.c) / (rect.b)

                    //check if stripping zone is reached after last vertical dip
                    check = sideofPoint(x_curr, y_curr, q_ln.a, q_ln.b, q_ln.c)
                    if (check === 'left') {
                        stage = 'strip'
                        continue
                    }
                    step_pts.push({ x: x_curr, y: y_curr })

                    //if stripping zone is not reached
                    next = step_intersection(y_curr)
                    x_curr = next.x
                    y_curr = next.y
                    check = sideofPoint(x_curr, y_curr, q_ln.a, q_ln.b, q_ln.c)
                    if (check === 'left') {
                        step_pts.push({ x: x_curr, y: y_curr })
                        stage = 'strip'
                        continue
                    }
                    step_pts.push({ x: x_curr, y: y_curr })

                    //vertical step
                    next = intersection_of_2lines(rect, { a: 1, b: 0, c: -1 * x_curr })
                    x_curr = next.x
                }

                if (stage === 'strip') {
                    //horizontal step
                    y_curr = (-1) * (strip.a * x_curr + strip.c) / (strip.b)

                    if (x_curr < xW) {
                        step_pts.push({ x: x_curr, y: x_curr })
                        break
                    }
                    step_pts.push({ x: x_curr, y: y_curr })

                    next = step_intersection(y_curr, equi)
                    x_curr = next.x
                    y_curr = next.y
                    step_pts.push({ x: x_curr, y: y_curr })

                    //vertical step
                    next = intersection_of_2lines(strip, { a: 1, b: 0, c: -1 * x_curr })
                    x_curr = next.x
                }
            }
            setSteps(step_pts)
            setStages(() => Math.floor(step_pts.length / 2))
        }
        catch (err) {
            console.log(err)
            setErr('Error in step calculation')
        }
    }

    return (
        <>

        </>
    )
}

Mccabe_plot.jsx

import { Line } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js';
import { useState, useEffect } from 'react';

export default function Mccabe_plot({ xW, xD, xF, fdInter, equi, steps, err, vol }) {

    ChartJS.register(
        CategoryScale,
        LinearScale,
        PointElement,
        LineElement,
        Title,
        Tooltip,
        Legend
    );

    const [x_eq, addx_eq] = useState([]);
    const [y_eq, addy_eq] = useState([]);
    const [x_steps, addx_steps] = useState([]);
    const [y_steps, addy_steps] = useState([]);
    const [chartData, setChartData] = useState(null);

    useEffect(() => {
        if (xD < 1 && xD>0 && xW > 0 && xW<1 && xF > 0 && xF < 1 && xD>xF && xF>xW) {
            if((vol==='vol' && err === '')||(vol==='datapts')){
    
                setChartData(equi.length>=5?{
                    datasets: [
                        {
                            label: 'x_eq vs y_eq',
                            data: equi.map((point) => ({
                                x: Number(point.x).toFixed(4),
                                y: Number(point.y).toFixed(4),
                            })),
                            fill: false,
                            borderColor: 'rgba(75, 192, 192, 1)',
                            tension: 0.1,
                        },
                        {
                            label: 'Steps',
                            data: steps.map((point) => ({
                                x: Number(point.x).toFixed(4),
                                y: Number(point.y).toFixed(4),
                            })),
                            fill: false,
                            borderColor: 'brown',
                            tension: 0.1,
                        },
                        {
                            label: 'Enriching Line',
                            data: [
                                { x: xD, y: xD },
                                { x: fdInter.x, y: fdInter.y },
                            ],
                            fill: true,
                            borderColor: 'red',
                            tension: 0.1,
                        },
                        {
                            label: 'Stripping Line',
                            data: [
                                { x: xW, y: xW },
                                { x: fdInter.x, y: fdInter.y },
                            ],
                            fill: true,
                            borderColor: 'blue',
                            tension: 0.1,
                        },
                        {
                            label: 'Feed Line',
                            data: [
                                { x: xF, y: xF },
                                { x: fdInter.x, y: fdInter.y },
                            ],
                            fill: true,
                            borderColor: 'grey',
                            tension: 0.1,
                        },
                        {
                            label: 'x=y',
                            data: [
                                { x: 0, y: 0 },
                                { x: 1, y: 1 },
                            ],
                            fill: true,
                            borderColor: 'black',
                            tension: 0.1,
                        }
                    ]
                }:
                {
                    datasets: [
                        {
                            label: 'x_eq vs y_eq',
                            data: equi.map((point) => ({
                                x: Number(point.x).toFixed(4),
                                y: Number(point.y).toFixed(4),
                            })),
                            fill: false,
                            borderColor: 'rgba(75, 192, 192, 1)',
                            tension: 0.1,
                        },
                        {
                            label: 'Enriching Line',
                            data: [
                                { x: xD, y: xD },
                                { x: fdInter.x, y: fdInter.y },
                            ],
                            fill: true,
                            borderColor: 'red',
                            tension: 0.1,
                        },
                        {
                            label: 'Stripping Line',
                            data: [
                                { x: xW, y: xW },
                                { x: fdInter.x, y: fdInter.y },
                            ],
                            fill: true,
                            borderColor: 'blue',
                            tension: 0.1,
                        },
                        {
                            label: 'Feed Line',
                            data: [
                                { x: xF, y: xF },
                                { x: fdInter.x, y: fdInter.y },
                            ],
                            fill: true,
                            borderColor: 'grey',
                            tension: 0.1,
                        },
                        {
                            label: 'x=y',
                            data: [
                                { x: 0, y: 0 },
                                { x: 1, y: 1 },
                            ],
                            fill: true,
                            borderColor: 'black',
                            tension: 0.1,
                        }
                    ]
                });
            }
            }
    }, [equi, steps, err, xD, xW, xF, vol]);

    const options = {
        responsive: true,
        scales: {
            x: {
                type: 'linear',
                position: 'bottom',
                title: {
                    display: true,
                    text: 'X',
                    font: {
                        size: 15,
                        weight: 'normal',
                    },
                    grid: {
                        borderColor: 'black',
                        borderWidth: 5,
                    },
                }
            },
            y: {
                beginAtZero: true,
                title: {
                    display: true,
                    text: 'Y',
                    font: {
                        size: 15,
                        weight: 'normal',
                    },
                },
            },
        },
    };

    return (
        <div>
            {chartData ? (
                <Line data={chartData} options={options} width={600} height={570} />
            ) : (
                <p>Invalid input values. Chart not updated.</p>
            )}
        </div>
    );
}

发布评论

评论列表(0)

  1. 暂无评论