calcVerticalStress.mjs

import map from 'lodash-es/map.js'
import get from 'lodash-es/get.js'
import sortBy from 'lodash-es/sortBy.js'
import cloneDeep from 'lodash-es/cloneDeep.js'
import size from 'lodash-es/size.js'
import join from 'lodash-es/join.js'
import cdbl from 'wsemi/src/cdbl.mjs'
import isnum from 'wsemi/src/isnum.mjs'
import isearr from 'wsemi/src/isearr.mjs'
import isestr from 'wsemi/src/isestr.mjs'
import cnst from './cnst.mjs'
import checkDepthStartEnd from './checkDepthStartEnd.mjs'


//rw, 水單位重(kN/m3)
let rw = cnst.rw


function checkVerticalStress(v, depth, unit, lable = '') {

    //check v
    if (!isnum(v)) {
        throw new Error(`v[${v}] is not a number`)
    }
    v = cdbl(v)

    //check depth
    if (!isnum(depth)) {
        throw new Error(`depth[${depth}] is not a number`)
    }
    depth = cdbl(depth)

    //check unit
    if (unit !== 'kPa' && unit !== 'MPa') {
        throw new Error(`unit[${unit}] need kPa or MPa`)
    }

    //最大最小單位重
    let rmin = 2 //kN/m3, 若最小飽和單位重12, 扣水單位重9.81, 最小乾單位重至少要低於2
    let rmax = 25 //kN/m3

    //最大最小應力
    let vmin = rmin * depth //kPa
    let vmax = rmax * depth //kPa

    //unit
    if (unit === 'MPa') {
        vmin /= 1000
        vmax /= 1000
    }

    //lable
    if (!isestr(lable)) {
        lable = `v`
    }

    //check
    if (v < vmin) {
        throw new Error(`${lable}[${v}](${unit}) < min[${vmin}](${unit}) in depth[${depth}](m)`)
        // console.log(`${lable}[${v}](${unit}) < min[${vmin}](${unit}) in depth[${depth}](m)`)
    }
    if (v > vmax) {
        throw new Error(`${lable}[${v}](${unit}) > max[${vmax}](${unit}) in depth[${depth}](m)`)
        // console.log(`${lable}[${v}](${unit}) > max[${vmax}](${unit}) in depth[${depth}](m)`)
    }

}


function core(rows, waterLevel, opt = {}) {

    //keyDepth
    let keyDepth = get(opt, 'keyDepth')
    if (!isestr(keyDepth)) {
        keyDepth = 'depth'
    }

    //keyDepthStart
    let keyDepthStart = get(opt, 'keyDepthStart')
    if (!isestr(keyDepthStart)) {
        keyDepthStart = 'depthStart'
    }

    //keyDepthEnd
    let keyDepthEnd = get(opt, 'keyDepthEnd')
    if (!isestr(keyDepthEnd)) {
        keyDepthEnd = 'depthEnd'
    }

    //cloneDeep
    rows = cloneDeep(rows)

    //each
    let sv_bottom = 0
    rows = map(rows, (v, k) => {

        //err
        let err = get(v, 'err', '')

        //ds, de
        let ds = get(v, keyDepthStart, null)
        let de = get(v, keyDepthEnd, null)

        try {

            //check
            if (!isnum(ds)) {
                throw new Error(`第 ${k} 樣本起始深度(${keyDepthStart})非數值: ${ds}`)
            }
            if (!isnum(de)) {
                throw new Error(`第 ${k} 樣本結束深度(${keyDepthEnd})非數值: ${de}`)
            }
            ds = cdbl(ds)
            de = cdbl(de)

            //depth, 土層中點深度(m)
            v[keyDepth] = (de + ds) / 2

            //rT, 使用單位重(kN/m3)
            let rT = null
            if (ds < waterLevel && de > waterLevel) { //樣本半乾半飽和
                let rsat = get(v, 'rsat', null) //飽和單位重(kN/m3)
                if (!isnum(rsat)) {
                    throw new Error(`第 ${k} 樣本飽和單位重(rsat)非數值: ${rsat}`)
                }
                let rd = get(v, 'rd', null) //乾單位重(kN/m3)
                if (!isnum(rd)) {
                    throw new Error(`第 ${k} 樣本乾單位重(rd)非數值: ${rd}`)
                }
                rd = cdbl(rd)
                let u = waterLevel - ds
                let d = de - waterLevel
                rT = (u * rd + d * rsat) / (u + d) //平均單位重
            }
            else if (de <= waterLevel) { //樣本結束深度小於等於地下水位, 代表整個樣本都在水上, 全乾
                let rd = get(v, 'rd', null) //乾單位重(kN/m3)
                if (!isnum(rd)) {
                    throw new Error(`第 ${k} 樣本乾單位重(rd)非數值: ${rd}`)
                }
                rT = rd
            }
            else if (ds >= waterLevel) { //樣本起始深度大於等於地下水位, 代表整個樣本都在水裡, 飽和
                let rsat = get(v, 'rsat', null)
                if (!isnum(rsat)) {
                    throw new Error(`第 ${k} 樣本飽和單位重(rsat)非數值: ${rsat}`)
                }
                rT = rsat
            }
            else {
                throw new Error('深度條件出現非預期情形')
            }

            //層厚度(m)
            let dd = (de - ds)

            //check sv_bottom
            if (sv_bottom === 0 && ds > 0) {
                //若第1筆數據深度>0, 則sv_bottom得要暴力計算基礎值, 否則會缺覆土應力
                sv_bottom = ds * rT
            }

            //sv(kN/m2), 土層中點深度之垂直總應力
            let dsv = dd * rT
            let sv = sv_bottom + dsv / 2
            v.sv = sv
            // console.log('sv', sv)
            // if (de <= 0.011) {
            //     console.log('dd', dd, 'rT', rT, 'sv_bottom', sv_bottom, 'sv', sv)
            // }

            //更新土層底部之垂直總應力
            sv_bottom += dsv

            //dpp(m), 土層中點深度之水頭高
            let dpp = Math.max(((ds + de) / 2 - waterLevel), 0)
            // console.log('dpp', dpp)
            // if (de <= 0.011) {
            //     console.log('ds', ds, 'de', de, 'waterLevel', waterLevel, 'dpp', dpp)
            // }

            //pp(kN/m2), 土層中點深度之水壓
            let pp = dpp * rw
            // console.log('pp', pp)

            //svp(kN/m2), 土層中點深度之垂直有效應力
            v.svp = Math.max(sv - pp, 0)
            // console.log('svp', v.svp)
            // if (de <= 0.011) {
            //     console.log('v.sv', v.sv, 'pp', pp, v.svp, 'v.svp')
            // }

        }
        catch (e) {
            let ce = e.toString()
            if (err === '') {
                err = ce
            }
            else {
                if (err.indexOf(ce) < 0) { //若不存在才加入
                    err += `, ${ce}`
                }
            }
            v.sv = ''
            v.svp = ''
            v.err = err
        }

        return v
    })

    return rows
}


/**
 * 計算樣本數據垂直總應力與有效應力
 *
 * Unit Test: {@link https://github.com/yuda-lyu/w-geo/blob/master/test/checkDepthStartEnd.test.js Github}
 * @memberOf w-geo
 * @param {Array} rows 輸入數據陣列,各數據為物件,至少需包含起始深度(depthStart,單位m)與結束深度(depthEnd,單位m),位於地下水位以上之樣本需提供乾單位重(rd,單位kN/m3),位於地下水位以下之樣本需提供飽和單位重(rsat,單位kN/m3),若地下水位位於該樣本起訖深度內,則需同時提供乾單位重與飽和單位重
 * @param {Object} [opt={}] 輸入設定物件,預設{}
 * @param {String} [opt.keyDepth='depth'] 輸入中點深度欄位鍵值字串,預設'depth'
 * @param {String} [opt.keyDepthStart='depthStart'] 輸入起始深度欄位鍵值字串,預設'depthStart'
 * @param {String} [opt.keyDepthEnd='depthEnd'] 輸入結束深度欄位鍵值字串,預設'depthEnd'
 * @param {Number} [opt.waterLevelUsual=0] 輸入常時地下水位數字,單位m,預設0
 * @param {Number} [opt.waterLevelDesign=0] 輸入設計地下水位數字,單位m,預設0
 * @returns {Array} 回傳計算後數據陣列
 * @example
 *
 * let waterLevelUsual
 * let waterLevelDesign
 * let rows
 * let rowsNew
 *
 * waterLevelUsual = 0
 * waterLevelDesign = 0
 * rows = [
 *     {
 *         depthStart: 0,
 *         depthEnd: 5,
 *         rsat: 18, //kN/m3
 *     },
 *     {
 *         depthStart: 5,
 *         depthEnd: 10,
 *         rsat: 18, //kN/m3
 *     },
 *     {
 *         depthStart: 10,
 *         depthEnd: 20,
 *         rsat: 18, //kN/m3
 *     },
 * ]
 * rowsNew = calcVerticalStress(rows, { waterLevelUsual, waterLevelDesign, unitSvSvp: 'kPa' })
 * console.log(rowsNew, (18 - 9.81) * 15) //地下 15(m) 處之垂直有效應力為 122.85(kN/m2)
 * // => [
 * //   {
 * //     depthStart: 0,
 * //     depthEnd: 5,
 * //     rsat: 18,
 * //     waterLevelUsual: 0,
 * //     waterLevelDesign: 0,
 * //     sv: 45,
 * //     svpUsual: 20.474999999999998,
 * //     svpDesign: 20.474999999999998,
 * //     depth: 2.5
 * //   },
 * //   {
 * //     depthStart: 5,
 * //     depthEnd: 10,
 * //     rsat: 18,
 * //     waterLevelUsual: 0,
 * //     waterLevelDesign: 0,
 * //     sv: 135,
 * //     svpUsual: 61.425,
 * //     svpDesign: 61.425,
 * //     depth: 7.5
 * //   },
 * //   {
 * //     depthStart: 10,
 * //     depthEnd: 20,
 * //     rsat: 18,
 * //     waterLevelUsual: 0,
 * //     waterLevelDesign: 0,
 * //     sv: 270,
 * //     svpUsual: 122.85,
 * //     svpDesign: 122.85,
 * //     depth: 15
 * //   }
 * // ] 122.85
 *
 * waterLevelUsual = 0
 * waterLevelDesign = 0
 * rows = [
 *     {
 *         depthStart: 0,
 *         depthEnd: 5,
 *         rsat: 18, //kN/m3
 *     },
 *     {
 *         depthStart: 5,
 *         depthEnd: 10,
 *         rsat: 19, //kN/m3
 *     },
 *     {
 *         depthStart: 10,
 *         depthEnd: 20,
 *         rsat: 20, //kN/m3
 *     },
 * ]
 * rowsNew = calcVerticalStress(rows, { waterLevelUsual, waterLevelDesign, unitSvSvp: 'kPa' })
 * console.log(rowsNew, (19 - 9.81) * 15) //地下 15(m) 處之垂直有效應力為 137.85(kN/m2)
 * // => [
 * //   {
 * //     depthStart: 0,
 * //     depthEnd: 5,
 * //     rsat: 18,
 * //     waterLevelUsual: 0,
 * //     waterLevelDesign: 0,
 * //     sv: 45,
 * //     svpUsual: 20.474999999999998,
 * //     svpDesign: 20.474999999999998,
 * //     depth: 2.5
 * //   },
 * //   {
 * //     depthStart: 5,
 * //     depthEnd: 10,
 * //     rsat: 19,
 * //     waterLevelUsual: 0,
 * //     waterLevelDesign: 0,
 * //     sv: 137.5,
 * //     svpUsual: 63.925,
 * //     svpDesign: 63.925,
 * //     depth: 7.5
 * //   },
 * //   {
 * //     depthStart: 10,
 * //     depthEnd: 20,
 * //     rsat: 20,
 * //     waterLevelUsual: 0,
 * //     waterLevelDesign: 0,
 * //     sv: 285,
 * //     svpUsual: 137.85,
 * //     svpDesign: 137.85,
 * //     depth: 15
 * //   }
 * // ] 137.85
 *
 * waterLevelUsual = 20
 * waterLevelDesign = 20
 * rows = [
 *     {
 *         depthStart: 0,
 *         depthEnd: 5,
 *         rd: 18, //kN/m3
 *     },
 *     {
 *         depthStart: 5,
 *         depthEnd: 10,
 *         rd: 18, //kN/m3
 *     },
 *     {
 *         depthStart: 10,
 *         depthEnd: 20,
 *         rd: 18, //kN/m3
 *     },
 * ]
 * rowsNew = calcVerticalStress(rows, { waterLevelUsual, waterLevelDesign, unitSvSvp: 'kPa' })
 * console.log(rowsNew, (18) * 15) //地下 15(m) 處之垂直總應力與垂直有效應力為 270(kN/m2)
 * // => [
 * //   {
 * //     depthStart: 0,
 * //     depthEnd: 5,
 * //     rd: 18,
 * //     waterLevelUsual: 20,
 * //     waterLevelDesign: 20,
 * //     sv: 45,
 * //     svpUsual: 45,
 * //     svpDesign: 45,
 * //     depth: 2.5
 * //   },
 * //   {
 * //     depthStart: 5,
 * //     depthEnd: 10,
 * //     rd: 18,
 * //     waterLevelUsual: 20,
 * //     waterLevelDesign: 20,
 * //     sv: 135,
 * //     svpUsual: 135,
 * //     svpDesign: 135,
 * //     depth: 7.5
 * //   },
 * //   {
 * //     depthStart: 10,
 * //     depthEnd: 20,
 * //     rd: 18,
 * //     waterLevelUsual: 20,
 * //     waterLevelDesign: 20,
 * //     sv: 270,
 * //     svpUsual: 270,
 * //     svpDesign: 270,
 * //     depth: 15
 * //   }
 * // ] 270
 *
 * waterLevelUsual = 3
 * waterLevelDesign = 3
 * rows = [
 *     {
 *         depthStart: 0,
 *         depthEnd: 3,
 *         rd: 18, //kN/m3
 *     },
 *     {
 *         depthStart: 3,
 *         depthEnd: 11,
 *         rsat: 20, //kN/m3
 *     },
 * ]
 * rowsNew = calcVerticalStress(rows, { waterLevelUsual, waterLevelDesign, unitSvSvp: 'kPa' })
 * console.log(rowsNew, 18 * 3 + (20 * 4 - 9.81 * 4)) //地下 7(m) 處之垂直有效應力為 94.76(kN/m2)
 * // => [
 * //   {
 * //     depthStart: 0,
 * //     depthEnd: 3,
 * //     rd: 18,
 * //     waterLevelUsual: 3,
 * //     waterLevelDesign: 3,
 * //     sv: 27,
 * //     svpUsual: 27,
 * //     svpDesign: 27,
 * //     depth: 1.5
 * //   },
 * //   {
 * //     depthStart: 3,
 * //     depthEnd: 11,
 * //     rsat: 20,
 * //     waterLevelUsual: 3,
 * //     waterLevelDesign: 3,
 * //     sv: 134,
 * //     svpUsual: 94.75999999999999,
 * //     svpDesign: 94.75999999999999,
 * //     depth: 7
 * //   }
 * // ] 94.75999999999999
 *
 * waterLevelUsual = 3
 * waterLevelDesign = 3
 * rows = [
 *     {
 *         depthStart: 0,
 *         depthEnd: 1,
 *         rd: 18, //kN/m3
 *     },
 *     {
 *         depthStart: 1,
 *         depthEnd: 5,
 *         rd: 18, //kN/m3
 *         rsat: 20, //kN/m3
 *     },
 *     {
 *         depthStart: 5,
 *         depthEnd: 9,
 *         rsat: 20, //kN/m3
 *     },
 * ]
 * rowsNew = calcVerticalStress(rows, { waterLevelUsual, waterLevelDesign, unitSvSvp: 'kPa' })
 * console.log(rowsNew, 18 * 3 + (20 * 4 - 9.81 * 4)) //地下 7(m) 處之垂直有效應力為 94.76(kN/m2)
 * // => [
 * //   {
 * //     depthStart: 0,
 * //     depthEnd: 1,
 * //     rd: 18,
 * //     waterLevelUsual: 3,
 * //     waterLevelDesign: 3,
 * //     sv: 9,
 * //     svpUsual: 9,
 * //     svpDesign: 9,
 * //     depth: 0.5
 * //   },
 * //   {
 * //     depthStart: 1,
 * //     depthEnd: 5,
 * //     rd: 18,
 * //     rsat: 20,
 * //     waterLevelUsual: 3,
 * //     waterLevelDesign: 3,
 * //     sv: 56,
 * //     svpUsual: 56,
 * //     svpDesign: 56,
 * //     depth: 3
 * //   },
 * //   {
 * //     depthStart: 5,
 * //     depthEnd: 9,
 * //     rsat: 20,
 * //     waterLevelUsual: 3,
 * //     waterLevelDesign: 3,
 * //     sv: 134,
 * //     svpUsual: 94.75999999999999,
 * //     svpDesign: 94.75999999999999,
 * //     depth: 7
 * //   }
 * // ] 94.75999999999999
 *
 * waterLevelUsual = 3
 * waterLevelDesign = 0
 * rows = [
 *     {
 *         depthStart: 0,
 *         depthEnd: 1,
 *         rd: 18, //kN/m3
 *         rsat: 20, //kN/m3
 *     },
 *     {
 *         depthStart: 1,
 *         depthEnd: 5,
 *         rd: 18, //kN/m3
 *         rsat: 20, //kN/m3
 *     },
 *     {
 *         depthStart: 5,
 *         depthEnd: 9,
 *         rsat: 20, //kN/m3
 *     },
 * ]
 * rowsNew = calcVerticalStress(rows, { waterLevelUsual, waterLevelDesign, unitSvSvp: 'kPa' })
 * console.log(rowsNew, 18 * 3 + (20 * 4 - 9.81 * 4), (20 - 9.81) * 7) //地下 7(m) 處之常時垂直有效應力為 94.76(kN/m2), 設計垂直有效應力為 71.33(kN/m2)
 * // => [
 * //   {
 * //     depthStart: 0,
 * //     depthEnd: 1,
 * //     rd: 18,
 * //     rsat: 20,
 * //     waterLevelUsual: 3,
 * //     waterLevelDesign: 0,
 * //     sv: 9,
 * //     svpUsual: 9,
 * //     svpDesign: 5.095,
 * //     depth: 0.5
 * //   },
 * //   {
 * //     depthStart: 1,
 * //     depthEnd: 5,
 * //     rd: 18,
 * //     rsat: 20,
 * //     waterLevelUsual: 3,
 * //     waterLevelDesign: 0,
 * //     sv: 56,
 * //     svpUsual: 56,
 * //     svpDesign: 30.57,
 * //     depth: 3
 * //   },
 * //   {
 * //     depthStart: 5,
 * //     depthEnd: 9,
 * //     rsat: 20,
 * //     waterLevelUsual: 3,
 * //     waterLevelDesign: 0,
 * //     sv: 134,
 * //     svpUsual: 94.75999999999999,
 * //     svpDesign: 71.33,
 * //     depth: 7
 * //   }
 * // ] 94.75999999999999 71.33
 *
 */
function calcVerticalStress(rows, opt = {}) {

    //check
    if (!isearr(rows)) {
        throw new Error(`無有效數據`)
    }

    //unitSvSvp
    let unitSvSvp = get(opt, 'unitSvSvp')
    if (unitSvSvp !== 'kPa' && unitSvSvp !== 'MPa') {
        throw new Error(`opt.unitSvSvp[${unitSvSvp}] need kPa or MPa`)
    }

    //keyDepth
    let keyDepth = get(opt, 'keyDepth')
    if (!isestr(keyDepth)) {
        keyDepth = 'depth'
    }

    //keyDepthStart
    let keyDepthStart = get(opt, 'keyDepthStart')
    if (!isestr(keyDepthStart)) {
        keyDepthStart = 'depthStart'
    }

    //keyDepthEnd
    let keyDepthEnd = get(opt, 'keyDepthEnd')
    if (!isestr(keyDepthEnd)) {
        keyDepthEnd = 'depthEnd'
    }

    //cloneDeep
    rows = cloneDeep(rows)

    //checkDepthStartEnd
    let ckds = checkDepthStartEnd(rows, opt)
    if (size(ckds) > 0) {
        throw new Error(join(ckds, '; '))
    }

    //sortBy
    rows = sortBy(rows, (v) => {
        return cdbl(v[keyDepthStart])
    })

    //waterLevelUsual
    let waterLevelUsual = get(opt, 'waterLevelUsual')
    if (!isnum(waterLevelUsual)) {
        waterLevelUsual = 0
    }
    waterLevelUsual = cdbl(waterLevelUsual)

    //waterLevelDesign
    let waterLevelDesign = get(opt, 'waterLevelDesign')
    if (!isnum(waterLevelDesign)) {
        waterLevelDesign = 0
    }
    waterLevelDesign = cdbl(waterLevelDesign)

    //usual
    let rowsUsual = core(rows, waterLevelUsual, { keyDepth, keyDepthStart, keyDepthEnd })
    // console.log('rowsUsual[0]', rowsUsual[0])

    //design
    let rowsDesign = core(rows, waterLevelDesign, { keyDepth, keyDepthStart, keyDepthEnd })
    // console.log('rowsDesign[0]', rowsDesign[0])

    //each
    rows = map(rows, (v, k) => {

        //save water
        v.waterLevelUsual = waterLevelUsual
        v.waterLevelDesign = waterLevelDesign

        //sv, svpUsual, svpDesign(kN/m2)
        let vUsual = rowsUsual[k]
        let vDesign = rowsDesign[k]
        v.sv = vUsual.sv //地下水位差異僅影響垂直有效應力, 故垂直總應力使用常時資料
        v.svpUsual = vUsual.svp //主要是給液化分析時CN與CRR使用, 其需使用鑽探時地下水位所計算得垂直有效應力
        v.svpDesign = vDesign.svp
        v[keyDepth] = vUsual[keyDepth] //重算中點深度

        return v
    })

    //unitSvSvp, 此處限制用kPa, 故指定為輸出MPa才要轉MPa
    if (unitSvSvp === 'MPa') {
        rows = map(rows, (v) => {
            v.sv /= 1000
            v.svpUsual /= 1000
            v.svpDesign /= 1000
            return v
        })
    }

    return rows
}


export {
    checkVerticalStress,
    calcVerticalStress
}
export default { //整合輸出預設得要有default
    checkVerticalStress,
    calcVerticalStress
}