diff --git a/dependencies/machine/machine.js b/dependencies/machine/machine.js new file mode 100644 index 0000000..bbd2fab --- /dev/null +++ b/dependencies/machine/machine.js @@ -0,0 +1,814 @@ +/** + * @file machine.js + * + * Permission is hereby granted to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to use it for personal + * or non-commercial purposes, with the following restrictions: + * + * 1. **No Copying or Redistribution**: The Software or any of its parts may not + * be copied, merged, distributed, sublicensed, or sold without explicit + * prior written permission from the author. + * + * 2. **Commercial Use**: Any use of the Software for commercial purposes requires + * a valid license, obtainable only with the explicit consent of the author. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, + * OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Ownership of this code remains solely with the original author. Unauthorized + * use of this Software is strictly prohibited. + * + * @summary A class to interact and manipulate machines with a non-euclidian curve + * @description A class to interact and manipulate machines with a non-euclidian curve + * @module machine + * @exports machine + * @version 0.1.0 + * @since 0.1.0 + * + * Author: + * - Rene De Ren + * Email: + * - rene@thegoldenbasket.nl + * + * Add functionality later + // -------- Operational Metrics -------- // +maintenanceAlert: this.state.checkMaintenanceStatus() + + +*/ + +//load local dependencies +const EventEmitter = require('events'); +const Logger = require('../../../generalFunctions/helper/logger'); +const State = require('../../../generalFunctions/helper/state/state'); +const Predict = require('../../../predict/dependencies/predict/predict_class'); +const { MeasurementContainer } = require('../../../generalFunctions/helper/measurements/index'); +const Interpolation = require('../../../predict/dependencies/predict/interpolation'); + +//load all config modules +const defaultConfig = require('../rotatingMachine/rotatingMachineConfig.json'); +const ConfigUtils = require('../../../generalFunctions/helper/configUtils'); + +//load registration utility +const ChildRegistrationUtils = require('../../../generalFunctions/helper/childRegistrationUtils'); +const ErrorMetrics = require('../../../generalFunctions/helper/nrmse/errorMetrics'); + +class Machine { + + /*------------------- Construct and set vars -------------------*/ + constructor(machineConfig = {}, stateConfig = {}, errorMetricsConfig = {}) { + + //basic setup + this.emitter = new EventEmitter(); // Own EventEmitter + this.configUtils = new ConfigUtils(defaultConfig); + this.config = this.configUtils.initConfig(machineConfig); + + // Initialize measurements + this.measurements = new MeasurementContainer(); + this.interpolation = new Interpolation(); + this.child = {}; // object to hold child information so we know on what to subscribe + + this.flowDrift = null; + + // Init after config is set + this.logger = new Logger(this.config.general.logging.enabled, this.config.general.logging.logLevel, this.config.general.name); + this.state = new State(stateConfig, this.logger); // Init State manager and pass logger + this.errorMetrics = new ErrorMetrics(errorMetricsConfig, this.logger); + + this.predictFlow = new Predict({ curve: this.config.asset.machineCurve.nq }); // load nq (x : ctrl , y : flow relationship) + this.predictPower = new Predict({ curve: this.config.asset.machineCurve.np }); // load np (x : ctrl , y : power relationship) + this.predictCtrl = new Predict({ curve: this.reverseCurve(this.config.asset.machineCurve.nq) }); // load reversed nq (x: flow, y: ctrl relationship) + + this.currentMode = this.config.mode.current; + this.currentEfficiencyCurve = {}; + this.cog = 0; + this.NCog = 0; + this.cogIndex = 0; + this.minEfficiency = 0; + this.absDistFromPeak = 0; + this.relDistFromPeak = 0; + + this.state.emitter.on("positionChange", (data) => { + this.logger.debug(`Position change detected: ${data}`); + this.updatePosition(); + }); + + + + + //this.calcCog(); + + + this.childRegistrationUtils = new ChildRegistrationUtils(this); // Child registration utility + + } + + // Method to assess drift using errorMetrics + assessDrift(measurement, processMin, processMax) { + this.logger.debug(`Assessing drift for measurement: ${measurement} processMin: ${processMin} processMax: ${processMax}`); + const predictedMeasurement = this.measurements.type(measurement).variant("predicted").position("downstream").getAllValues().values; + const measuredMeasurement = this.measurements.type(measurement).variant("measured").position("downstream").getAllValues().values; + + if (!predictedMeasurement || !measuredMeasurement) return null; + + return this.errorMetrics.assessDrift( + predictedMeasurement, + measuredMeasurement, + processMin, + processMax + ); + } + + reverseCurve(curve) { + const reversedCurve = {}; + for (const [pressure, values] of Object.entries(curve)) { + reversedCurve[pressure] = { + x: [...values.y], // Previous y becomes new x + y: [...values.x] // Previous x becomes new y + }; + } + return reversedCurve; + } + + // -------- Config -------- // + updateConfig(newConfig) { + this.config = this.configUtils.updateConfig(this.config, newConfig); + } + + // -------- Mode and Input Management -------- // + isValidSourceForMode(source, mode) { + const allowedSourcesSet = this.config.mode.allowedSources[mode] || []; + return allowedSourcesSet.has(source); + } + + isValidActionForMode(action, mode) { + const allowedActionsSet = this.config.mode.allowedActions[mode] || []; + return allowedActionsSet.has(action); + } + + async handleInput(source, action, parameter) { + if (!this.isValidSourceForMode(source, this.currentMode)) { + let warningTxt = `Source '${source}' is not valid for mode '${this.currentMode}'.`; + this.logger.warn(warningTxt); + return {status : false , feedback: warningTxt}; + } + + + this.logger.info(`Handling input from source '${source}' with action '${action}' in mode '${this.currentMode}'.`); + try { + switch (action) { + case "execSequence": + await this.executeSequence(parameter); + //recalc flow and power + this.updatePosition(); + break; + case "execMovement": + await this.setpoint(parameter); + break; + case "flowMovement": + // Calculate the control value for a desired flow + const pos = this.calcCtrl(parameter); + // Move to the desired setpoint + await this.setpoint(pos); + break; + case "emergencyStop": + this.logger.warn(`Emergency stop activated by '${source}'.`); + await this.executeSequence("emergencyStop"); + break; + case "statusCheck": + this.logger.info(`Status Check: Mode = '${this.currentMode}', Source = '${source}'.`); + break; + default: + this.logger.warn(`Action '${action}' is not implemented.`); + break; + } + this.logger.debug(`Action '${action}' successfully executed`); + return {status : true , feedback: `Action '${action}' successfully executed.`}; + } catch (error) { + this.logger.error(`Error handling input: ${error}`); + } + } + + setMode(newMode) { + const availableModes = defaultConfig.mode.current.rules.values.map(v => v.value); + if (!availableModes.includes(newMode)) { + this.logger.warn(`Invalid mode '${newMode}'. Allowed modes are: ${availableModes.join(', ')}`); + return; + } + + this.currentMode = newMode; + this.logger.info(`Mode successfully changed to '${newMode}'.`); + } + + // -------- Sequence Handlers -------- // + async executeSequence(sequenceName) { + + const sequence = this.config.sequences[sequenceName]; + + if (!sequence || sequence.size === 0) { + this.logger.warn(`Sequence '${sequenceName}' not defined.`); + return; + } + + if (this.state.getCurrentState() == "operational" && sequenceName == "shutdown") { + this.logger.info(`Machine will ramp down to position 0 before performing ${sequenceName} sequence`); + await this.setpoint(0); + } + + this.logger.info(` --------- Executing sequence: ${sequenceName} -------------`); + + for (const state of sequence) { + try { + await this.state.transitionToState(state); + // Update measurements after state change + + } catch (error) { + this.logger.error(`Error during sequence '${sequenceName}': ${error}`); + break; // Exit sequence execution on error + } + } + } + + async setpoint(setpoint) { + + try { + // Validate setpoint + if (typeof setpoint !== 'number' || setpoint < 0) { + throw new Error("Invalid setpoint: Setpoint must be a non-negative number."); + } + + // Move to the desired setpoint + await this.state.moveTo(setpoint); + + } catch (error) { + console.error(`Error setting setpoint: ${error}`); + } + } + + // Calculate flow based on current pressure and position + calcFlow(x) { + const state = this.state.getCurrentState(); + + if (!["operational", "accelerating", "decelerating"].includes(state)) { + this.measurements.type("flow").variant("predicted").position("downstream").value(0); + this.logger.debug(`Machine is not operational. Setting predicted flow to 0.`); + return 0; + } + + //this.predictFlow.currentX = x; Decrepated + const cFlow = this.predictFlow.y(x); + this.measurements.type("flow").variant("predicted").position("downstream").value(cFlow); + //this.logger.debug(`Calculated flow: ${cFlow} for pressure: ${this.getMeasuredPressure()} and position: ${x}`); + return cFlow; + + } + + // Calculate power based on current pressure and position + calcPower(x) { + const state = this.state.getCurrentState(); + if (!["operational", "accelerating", "decelerating"].includes(state)) { + this.measurements.type("power").variant("predicted").position('upstream').value(0); + this.logger.debug(`Machine is not operational. Setting predicted power to 0.`); + return 0; + } + + //this.predictPower.currentX = x; Decrepated + const cPower = this.predictPower.y(x); + this.measurements.type("power").variant("predicted").position('upstream').value(cPower); + //this.logger.debug(`Calculated power: ${cPower} for pressure: ${this.getMeasuredPressure()} and position: ${x}`); + return cPower; + } + + // calculate the power consumption using only flow and pressure + inputFlowCalcPower(flow) { + this.predictCtrl.currentX = flow; + const cCtrl = this.predictCtrl.y(flow); + this.predictPower.currentX = cCtrl; + const cPower = this.predictPower.y(cCtrl); + return cPower; + } + + // Function to predict control value for a desired flow + calcCtrl(x) { + + this.predictCtrl.currentX = x; + const cCtrl = this.predictCtrl.y(x); + this.measurements.type("ctrl").variant("predicted").position('upstream').value(cCtrl); + //this.logger.debug(`Calculated ctrl: ${cCtrl} for pressure: ${this.getMeasuredPressure()} and position: ${x}`); + return cCtrl; + + } + + // this function returns the pressure for calculations + getMeasuredPressure() { + const pressureDiff = this.measurements.type('pressure').variant('measured').difference(); + + // Both upstream & downstream => differential + if (pressureDiff != null) { + this.logger.debug(`Pressure differential: ${pressureDiff.value}`); + this.predictFlow.fDimension = pressureDiff.value; + this.predictPower.fDimension = pressureDiff.value; + this.predictCtrl.fDimension = pressureDiff.value; + //update the cog + const { cog, minEfficiency } = this.calcCog(); + // calc efficiency + const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted"); + //update the distance from peak + this.calcDistanceBEP(efficiency,cog,minEfficiency); + + return pressureDiff.value; + } + + // get downstream + const downstreamPressure = this.measurements.type('pressure').variant('measured').position('downstream').getCurrentValue(); + + // Only downstream => use it, warn that it's partial + if (downstreamPressure != null) { + this.logger.warn(`Using downstream pressure only for prediction: ${downstreamPressure} `); + this.predictFlow.fDimension = downstreamPressure; + this.predictPower.fDimension = downstreamPressure; + this.predictCtrl.fDimension = downstreamPressure; + //update the cog + const { cog, minEfficiency } = this.calcCog(); + // calc efficiency + const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted"); + //update the distance from peak + this.calcDistanceBEP(efficiency,cog,minEfficiency); + return downstreamPressure; + } + + this.logger.error(`No valid pressure measurements available to calculate prediction using last known pressure`); + + //set default at 0 => lowest pressure possible + this.predictFlow.fDimension = 0; + this.predictPower.fDimension = 0; + this.predictCtrl.fDimension = 0; + //update the cog + const { cog, minEfficiency } = this.calcCog(); + // calc efficiency + const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted"); + //update the distance from peak + this.calcDistanceBEP(efficiency,cog,minEfficiency); + return 0; + } + + handleMeasuredFlow() { + const flowDiff = this.measurements.type('flow').variant('measured').difference(); + + // If both are present + if (flowDiff != null) { + // In theory, mass flow in = mass flow out, so they should match or be close. + if (flowDiff.value < 0.001) { + // flows match within tolerance + this.logger.debug(`Flow match: ${flowDiff.value}`); + return flowDiff.value; + } else { + // Mismatch => decide how to handle. Maybe take the average? + // Or bail out with an error. Example: we bail out here. + this.logger.error(`Something wrong with down or upstream flow measurement. Bailing out!`); + return null; + } + } + + // get + const upstreamFlow = this.measurements.type('pressure').variant('measured').position('upstream').getCurrentValue(); + + // Only upstream => might still accept it, but warn + if (upstreamFlow != null) { + this.logger.warn(`Only upstream flow is present. Using it but results may be incomplete!`); + return upstreamFlow; + } + + // get + const downstreamFlow = this.measurements.type('pressure').variant('measured').position('downstream').getCurrentValue(); + + // Only downstream => might still accept it, but warn + if (downstreamFlow != null) { + this.logger.warn(`Only downstream flow is present. Using it but results may be incomplete!`); + return downstreamFlow; + } + + // Neither => error + this.logger.error(`No upstream or downstream flow measurement. Bailing out!`); + return null; + } + + handleMeasuredPower() { + const power = this.measurements.type("power").variant("measured").position("upstream").getCurrentValue(); + // If your system calls it "upstream" or just a single "value", adjust accordingly + + if (power != null) { + this.logger.debug(`Measured power: ${power}`); + return power; + } else { + this.logger.error(`No measured power found. Bailing out!`); + return null; + } + } + + updatePressure(variant,value,position) { + + switch (variant) { + case ("measured"): + //only update when machine is in a state where it can be used + if (this.state.getCurrentState() == "operational" || this.state.getCurrentState() == "accelerating" || this.state.getCurrentState() == "decelerating") { + // put value in measurements + this.measurements.type("pressure").variant("measured").position(position).value(value); + //when measured pressure gets updated we need some logic to fetch the relevant value which could be downstream or differential pressure + const pressure = this.getMeasuredPressure(); + //update the flow power and cog + this.updatePosition(); + this.logger.debug(`Measured pressure: ${pressure}`); + } + break; + + default: + this.logger.warn(`Unrecognized variant '${variant}' for pressure update.`); + break; + } + } + + updateFlow(variant,value,position) { + + switch (variant) { + case ("measured"): + // put value in measurements + this.measurements.type("flow").variant("measured").position(position).value(value); + //when measured flow gets updated we need to push the last known value in the prediction measurements to keep them synced + this.measurements.type("flow").variant("predicted").position("downstream").value(this.predictFlow.outputY); + break; + + case ("predicted"): + this.logger.debug('not doing anythin yet'); + break; + + default: + this.logger.warn(`Unrecognized variant '${variant}' for flow update.`); + break; + } + } + + updateMeasurement(variant, subType, value, position) { + this.logger.debug(`---------------------- updating ${subType} ------------------ `); + switch (subType) { + case "pressure": + // Update pressure measurement + this.updatePressure(variant,value,position); + break; + case "flow": + this.updateFlow(variant,value,position); + // Update flow measurement + this.flowDrift = this.assessDrift("flow", this.predictFlow.currentFxyYMin , this.predictFlow.currentFxyYMax); + this.logger.debug(`---------------------------------------- `); + break; + case "power": + // Update power measurement + break; + default: + this.logger.error(`Type '${type}' not recognized for measured update.`); + return; + } + } + + //what is the internal functions that need updating when something changes that has influence on this. + updatePosition() { + if (this.state.getCurrentState() == "operational" || this.state.getCurrentState() == "accelerating" || this.state.getCurrentState() == "decelerating") { + + const currentPosition = this.state.getCurrentPosition(); + + // Update the predicted values based on the new position + const { cPower, cFlow } = this.calcFlowPower(currentPosition); + + // Calc predicted efficiency + const efficiency = this.calcEfficiency(cPower, cFlow, "predicted"); + + //update the cog + const { cog, minEfficiency } = this.calcCog(); + + //update the distance from peak + this.calcDistanceBEP(efficiency,cog,minEfficiency); + + } + } + + calcDistanceFromPeak(currentEfficiency,peakEfficiency){ + return Math.abs(currentEfficiency - peakEfficiency); + } + + calcRelativeDistanceFromPeak(currentEfficiency,maxEfficiency,minEfficiency){ + let distance = 1; + if(currentEfficiency != null){ + distance = this.interpolation.interpolate_lin_single_point(currentEfficiency,maxEfficiency, minEfficiency, 0, 1); + } + return distance; + } + + // Calculate the center of gravity for current pressure + calcCog() { + + //fetch current curve data for power and flow + const { powerCurve, flowCurve } = this.getCurrentCurves(); + + const {efficiencyCurve, peak, peakIndex, minEfficiency } = this.calcEfficiencyCurve(powerCurve, flowCurve); + + // Calculate the normalized center of gravity + const NCog = (flowCurve.y[peakIndex] - this.predictFlow.currentFxyYMin) / (this.predictFlow.currentFxyYMax - this.predictFlow.currentFxyYMin); + + //store in object for later retrieval + this.currentEfficiencyCurve = efficiencyCurve; + this.cog = peak; + this.cogIndex = peakIndex; + this.NCog = NCog; + this.minEfficiency = minEfficiency; + + return { cog: peak, cogIndex: peakIndex, NCog: NCog, minEfficiency: minEfficiency }; + + } + + calcEfficiencyCurve(powerCurve, flowCurve) { + + const efficiencyCurve = []; + let peak = 0; + let peakIndex = 0; + let minEfficiency = 0; + + // Calculate efficiency curve based on power and flow curves + powerCurve.y.forEach((power, index) => { + + // Get flow for the current power + const flow = flowCurve.y[index]; + + // higher efficiency is better + efficiencyCurve.push( Math.round( ( flow / power ) * 100 ) / 100); + + // Keep track of peak efficiency + peak = Math.max(peak, efficiencyCurve[index]); + peakIndex = peak == efficiencyCurve[index] ? index : peakIndex; + minEfficiency = Math.min(...efficiencyCurve); + + }); + + return { efficiencyCurve, peak, peakIndex, minEfficiency }; + + } + + //calc flow power based on pressure and current position + calcFlowPower(x) { + + // Calculate flow and power + const cFlow = this.calcFlow(x); + const cPower = this.calcPower(x); + + return { cPower, cFlow }; + } + + calcEfficiency(power, flow, variant) { + + if (power != 0 && flow != 0) { + // Calculate efficiency after measurements update + this.measurements.type("efficiency").variant(variant).position('downstream').value((flow / power)); + } else { + this.measurements.type("efficiency").variant(variant).position('downstream').value(null); + } + + return this.measurements.type("efficiency").variant(variant).position('downstream').getCurrentValue(); + + } + + updateCurve(newCurve) { + this.logger.info(`Updating machine curve`); + const newConfig = { asset: { machineCurve: newCurve } }; + + //validate input of new curve fed to the machine + this.config = this.configUtils.updateConfig(this.config, newConfig); + + //After we passed validation load the curves into their predictors + this.predictFlow.updateCurve(this.config.asset.machineCurve.nq); + this.predictPower.updateCurve(this.config.asset.machineCurve.np); + this.predictCtrl.updateCurve(this.reverseCurve(this.config.asset.machineCurve.nq)); + } + + getCompleteCurve() { + const powerCurve = this.predictPower.inputCurveData; + const flowCurve = this.predictFlow.inputCurveData; + return { powerCurve, flowCurve }; + } + + getCurrentCurves() { + const powerCurve = this.predictPower.currentFxyCurve[this.predictPower.currentF]; + const flowCurve = this.predictFlow.currentFxyCurve[this.predictFlow.currentF]; + + return { powerCurve, flowCurve }; + + } + + calcDistanceBEP(efficiency,maxEfficiency,minEfficiency) { + + const absDistFromPeak = this.calcDistanceFromPeak(efficiency,maxEfficiency); + const relDistFromPeak = this.calcRelativeDistanceFromPeak(efficiency,maxEfficiency,minEfficiency); + + //store internally + this.absDistFromPeak = absDistFromPeak ; + this.relDistFromPeak = relDistFromPeak; + + return { absDistFromPeak: absDistFromPeak, relDistFromPeak: relDistFromPeak }; + } + + getOutput() { + + // Improved output object generation + const output = {}; + //build the output object + this.measurements.getTypes().forEach(type => { + this.measurements.getVariants(type).forEach(variant => { + + const downstreamVal = this.measurements.type(type).variant(variant).position("downstream").getCurrentValue(); + const upstreamVal = this.measurements.type(type).variant(variant).position("upstream").getCurrentValue(); + + if (downstreamVal != null) { + output[`downstream_${variant}_${type}`] = downstreamVal; + } + if (upstreamVal != null) { + output[`upstream_${variant}_${type}`] = upstreamVal; + } + if (downstreamVal != null && upstreamVal != null) { + const diffVal = this.measurements.type(type).variant(variant).difference().value; + output[`differential_${variant}_${type}`] = diffVal; + } + }); + }); + + //fill in the rest of the output object + output["state"] = this.state.getCurrentState(); + output["runtime"] = this.state.getRunTimeHours(); + output["ctrl"] = this.state.getCurrentPosition(); + output["moveTimeleft"] = this.state.getMoveTimeLeft(); + output["mode"] = this.currentMode; + output["cog"] = this.cog; // flow / power efficiency + output["NCog"] = this.NCog; // normalized cog + output["NCogPercent"] = Math.round(this.NCog * 100 * 100) / 100 ; + + if(this.flowDrift != null){ + const flowDrift = this.flowDrift; + output["flowNrmse"] = flowDrift.nrmse; + output["flowLongterNRMSD"] = flowDrift.longTermNRMSD; + output["flowImmediateLevel"] = flowDrift.immediateLevel; + output["flowLongTermLevel"] = flowDrift.longTermLevel; + } + + //should this all go in the container of measurements? + output["effDistFromPeak"] = this.absDistFromPeak; + output["effRelDistFromPeak"] = this.relDistFromPeak; + //this.logger.debug(`Output: ${JSON.stringify(output)}`); + + return output; + } + + +} // end of class + +module.exports = Machine; + +/*------------------- Testing -------------------*/ + +/* +curve = require('C:/Users/zn375/.node-red/public/fallbackData.json'); + +//import a child +const Child = require('../../../measurement/dependencies/measurement/measurement'); + +console.log(`Creating child...`); +const PT1 = new Child(config={ + general:{ + name:"PT1", + logging:{ + enabled:true, + logLevel:"debug", + }, + }, + functionality:{ + softwareType:"measurement", + }, + asset:{ + type:"sensor", + subType:"pressure", + }, +}); + +const PT2 = new Child(config={ + general:{ + name:"PT2", + logging:{ + enabled:true, + logLevel:"debug", + }, + }, + functionality:{ + softwareType:"measurement", + }, + asset:{ + type:"sensor", + subType:"pressure", + }, +}); + +//create a machine +console.log(`Creating machine...`); + +const machineConfig = { + general: { + name: "Hydrostal", + logging: { + enabled: true, + logLevel: "debug", + } + }, + asset: { + supplier: "Hydrostal", + type: "pump", + subType: "centrifugal", + model: "H05K-S03R+HGM1X-X280KO", // Ensure this field is present. + machineCurve: curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"], + } +} + +const stateConfig = { + general: { + logging: { + enabled: true, + logLevel: "debug", + }, + }, + // Your custom config here (or leave empty for defaults) + movement: { + speed: 1, + }, + time: { + starting: 2, + warmingup: 3, + stopping: 2, + coolingdown: 3, + }, +}; + +const machine = new Machine(machineConfig, stateConfig); + +//machine.logger.info(JSON.stringify(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"])); +machine.logger.info(`Registering child...`); +machine.childRegistrationUtils.registerChild(PT1, "upstream"); +machine.childRegistrationUtils.registerChild(PT2, "downstream"); + +//feed curve to the machine class +//machine.updateCurve(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]); + +PT1.logger.info(`Enable sim...`); +PT1.toggleSimulation(); +PT2.logger.info(`Enable sim...`); +PT2.toggleSimulation(); +machine.getOutput(); +//manual test +//machine.handleInput("parent", "execSequence", "startup"); + +machine.measurements.type("pressure").variant("measured").position('upstream').value(-200); +machine.measurements.type("pressure").variant("measured").position('downstream').value(1000); + +testingSequences(); + +const tickLoop = setInterval(changeInput,1000); + +function changeInput(){ + PT1.logger.info(`tick...`); + PT1.tick(); + PT2.tick(); +} + +async function testingSequences(){ + try{ + console.log(` ********** Testing sequence startup... **********`); + await machine.handleInput("parent", "execSequence", "startup"); + console.log(` ********** Testing movement to 15... **********`); + await machine.handleInput("parent", "execMovement", 15); + machine.getOutput(); + console.log(` ********** Testing sequence shutdown... **********`); + await machine.handleInput("parent", "execSequence", "shutdown"); + console.log(`********** Testing moving to setpoint 10... while in idle **********`); + await machine.handleInput("parent", "execMovement", 10); + console.log(` ********** Testing sequence emergencyStop... **********`); + await machine.handleInput("parent", "execSequence", "emergencystop"); + console.log(`********** Testing sequence boot... **********`); + await machine.handleInput("parent", "execSequence", "boot"); + }catch(error){ + console.error(`Error: ${error}`); + } +} + + +//*/ + + + diff --git a/dependencies/machine/machine.test.js b/dependencies/machine/machine.test.js new file mode 100644 index 0000000..6d60b03 --- /dev/null +++ b/dependencies/machine/machine.test.js @@ -0,0 +1,297 @@ +const Machine = require('./machine'); +const specs = require('../../../generalFunctions/datasets/assetData/pumps/hydrostal/centrifugal pumps/models.json'); + +class MachineTester { + constructor() { + this.totalTests = 0; + this.passedTests = 0; + this.failedTests = 0; + this.machineCurve = specs[0].machineCurve; + } + + assert(condition, message) { + this.totalTests++; + if (condition) { + console.log(`✓ PASS: ${message}`); + this.passedTests++; + } else { + console.log(`✗ FAIL: ${message}`); + this.failedTests++; + } + } + + createBaseMachineConfig(name) { + return { + general: { + logging: { enabled: true, logLevel: "debug" }, + name: name, + unit: "m3/h" + }, + functionality: { + softwareType: "machine", + role: "RotationalDeviceController" + }, + asset: { + type: "pump", + subType: "Centrifugal", + model: "TestModel", + supplier: "Hydrostal", + machineCurve: this.machineCurve + }, + mode: { + current: "auto", + allowedActions: { + auto: ["execSequence", "execMovement", "statusCheck"], + virtualControl: ["execMovement", "statusCheck"], + fysicalControl: ["statusCheck"] + }, + allowedSources: { + auto: ["parent", "GUI"], + virtualControl: ["GUI"], + fysicalControl: ["fysical"] + } + }, + sequences: { + startup: ["starting", "warmingup", "operational"], + shutdown: ["stopping", "coolingdown", "idle"], + emergencystop: ["emergencystop", "off"], + boot: ["idle", "starting", "warmingup", "operational"] + }, + calculationMode: "medium" + }; + } + + async testBasicOperations() { + console.log('\nTesting Basic Machine Operations...'); + + const machine = new Machine(this.createBaseMachineConfig("TestMachine")); + + try { + // Test 1: Initialization + this.assert( + machine.currentMode === "auto", + 'Machine should initialize in auto mode' + ); + + // Test 2: Set pressure measurement + machine.measurements.type("pressure").variant("measured").position("downstream").value(800); + const pressure = machine.handleMeasuredPressure(); + this.assert( + pressure === 800, + 'Should correctly handle pressure measurement' + ); + + // Test 3: State transition + await machine.state.transitionToState("idle"); + this.assert( + machine.state.getCurrentState() === "idle", + 'Should transition to idle state' + ); + + // Test 4: Mode change + machine.setMode("virtualControl"); + this.assert( + machine.currentMode === "virtualControl", + 'Should change mode to virtual control' + ); + } catch (error) { + console.error('Test failed with error:', error); + this.failedTests++; + } + } + + async testPredictions() { + console.log('\nTesting Machine Predictions...'); + + const machine = new Machine(this.createBaseMachineConfig("TestMachine")); + machine.measurements.type("pressure").variant("measured").position("downstream").value(800); + + try { + // Test 1: Flow prediction + const flow = machine.calcFlow(50); + this.assert( + flow > 0 && !isNaN(flow), + 'Should calculate valid flow for control value' + ); + + // Test 2: Power prediction + const power = machine.calcPower(50); + this.assert( + power > 0 && !isNaN(power), + 'Should calculate valid power for control value' + ); + + // Test 3: Control prediction + const ctrl = machine.calcCtrl(100); + this.assert( + ctrl >= 0 && ctrl <= 100, + 'Should calculate valid control value for desired flow' + ); + } catch (error) { + console.error('Test failed with error:', error); + this.failedTests++; + } + } + + async testSequenceExecution() { + console.log('\nTesting Machine Sequences...'); + + const machine = new Machine(this.createBaseMachineConfig("TestMachine")); + + try { + // Test 1: Startup sequence + await machine.handleInput("parent", "execSequence", "startup"); + this.assert( + machine.state.getCurrentState() === "operational", + 'Should complete startup sequence' + ); + + // Test 2: Movement after startup + await machine.handleInput("parent", "execMovement", 50); + this.assert( + machine.state.getCurrentPosition() === 50, + 'Should move to specified position' + ); + + // Test 3: Shutdown sequence + await machine.handleInput("parent", "execSequence", "shutdown"); + this.assert( + machine.state.getCurrentState() === "idle", + 'Should complete shutdown sequence' + ); + + // Test 4: Emergency stop + await machine.handleInput("parent", "execSequence", "emergencystop"); + this.assert( + machine.state.getCurrentState() === "off", + 'Should execute emergency stop' + ); + } catch (error) { + console.error('Test failed with error:', error); + this.failedTests++; + } + } + + async testMeasurementHandling() { + console.log('\nTesting Measurement Handling...'); + + const machine = new Machine(this.createBaseMachineConfig("TestMachine")); + + try { + // Test 1: Pressure measurement + machine.measurements.type("pressure").variant("measured").position("downstream").value(800); + machine.measurements.type("pressure").variant("measured").setUpstream(1000); + const pressure = machine.handleMeasuredPressure(); + this.assert( + pressure === 200, + 'Should calculate correct differential pressure' + ); + + // Test 2: Flow measurement + machine.measurements.type("flow").variant("measured").position("downstream").value(100); + const flow = machine.handleMeasuredFlow(); + this.assert( + flow === 100, + 'Should handle flow measurement correctly' + ); + + // Test 3: Power measurement + machine.measurements.type("power").variant("measured").setUpstream(75); + const power = machine.handleMeasuredPower(); + this.assert( + power === 75, + 'Should handle power measurement correctly' + ); + + // Test 4: Efficiency calculation + machine.calcEfficiency(); + const efficiency = machine.measurements.type("efficiency").variant("measured").getDownstream(); + this.assert( + efficiency > 0 && !isNaN(efficiency), + 'Should calculate valid efficiency' + ); + } catch (error) { + console.error('Test failed with error:', error); + this.failedTests++; + } + } + + async testCurveHandling() { + console.log('\nTesting Machine Curve Handling...'); + + const machine = new Machine(this.createBaseMachineConfig("TestMachine")); + + try { + // Test 1: Curve initialization + const curves = machine.showCurve(); + this.assert( + curves.powerCurve && curves.flowCurve, + 'Should properly initialize power and flow curves' + ); + + // Test 2: Test reverse curve creation + const reversedCurve = machine.reverseCurve(this.machineCurve.nq); + this.assert( + reversedCurve["1"].x[0] === this.machineCurve.nq["1"].y[0] && + reversedCurve["1"].y[0] === this.machineCurve.nq["1"].x[0], + 'Should correctly reverse x and y values in curve' + ); + + // Test 3: Update curve dynamically + const newCurve = { + nq: { + "1": { + x: [0, 25, 50, 75, 100], + y: [0, 125, 250, 375, 500] + } + }, + np: { + "1": { + x: [0, 25, 50, 75, 100], + y: [0, 75, 150, 225, 300] + } + } + }; + machine.updateCurve(newCurve); + const updatedCurves = machine.showCurve(); + this.assert( + updatedCurves.flowCurve["1"].y[2] === 250, + 'Should update curve with new values' + ); + + // Test 4: Verify curve interpolation + machine.measurements.type("pressure").variant("measured").position("downstream").value(800); + const midpointCtrl = machine.calcCtrl(250); // Should interpolate between points + const calculatedFlow = machine.calcFlow(midpointCtrl); + this.assert( + Math.abs(calculatedFlow - 250) < 1, // Allow small numerical error + 'Should accurately interpolate between curve points' + ); + + } catch (error) { + console.error('Test failed with error:', error); + this.failedTests++; + } + } + + async runAllTests() { + console.log('Starting Machine Tests...\n'); + + await this.testBasicOperations(); + await this.testPredictions(); + await this.testSequenceExecution(); + await this.testMeasurementHandling(); + await this.testCurveHandling(); + + console.log('\nTest Summary:'); + console.log(`Total Tests: ${this.totalTests}`); + console.log(`Passed: ${this.passedTests}`); + console.log(`Failed: ${this.failedTests}`); + + process.exit(this.failedTests > 0 ? 1 : 0); + } +} + +// Run the tests +const tester = new MachineTester(); +tester.runAllTests().catch(console.error); \ No newline at end of file diff --git a/dependencies/rotatingMachine/rotatingMachineConfig.json b/dependencies/rotatingMachine/rotatingMachineConfig.json new file mode 100644 index 0000000..dfc366b --- /dev/null +++ b/dependencies/rotatingMachine/rotatingMachineConfig.json @@ -0,0 +1,381 @@ +{ + "general": { + "name": { + "default": "Rotating Machine", + "rules": { + "type": "string", + "description": "A human-readable name or label for this machine configuration." + } + }, + "id": { + "default": null, + "rules": { + "type": "string", + "nullable": true, + "description": "A unique identifier for this configuration. If not provided, defaults to null." + } + }, + "unit": { + "default": "m3/h", + "rules": { + "type": "string", + "description": "The default measurement unit for this configuration (e.g., 'meters', 'seconds', 'unitless')." + } + }, + "logging": { + "logLevel": { + "default": "info", + "rules": { + "type": "enum", + "values": [ + { + "value": "debug", + "description": "Log messages are printed for debugging purposes." + }, + { + "value": "info", + "description": "Informational messages are printed." + }, + { + "value": "warn", + "description": "Warning messages are printed." + }, + { + "value": "error", + "description": "Error messages are printed." + } + ] + } + }, + "enabled": { + "default": true, + "rules": { + "type": "boolean", + "description": "Indicates whether logging is active. If true, log messages will be generated." + } + } + } + }, + "functionality": { + "softwareType": { + "default": "machine", + "rules": { + "type": "string", + "description": "Specified software type for this configuration." + } + }, + "role": { + "default": "RotationalDeviceController", + "rules": { + "type": "string", + "description": "Indicates the role this configuration plays within the system." + } + } + }, + "asset": { + "uuid": { + "default": null, + "rules": { + "type": "string", + "nullable": true, + "description": "A universally unique identifier for this asset. May be null if not assigned." + } + }, + "geoLocation": { + "default": {}, + "rules": { + "type": "object", + "description": "An object representing the asset's physical coordinates or location.", + "schema": { + "x": { + "default": 0, + "rules": { + "type": "number", + "description": "X coordinate of the asset's location." + } + }, + "y": { + "default": 0, + "rules": { + "type": "number", + "description": "Y coordinate of the asset's location." + } + }, + "z": { + "default": 0, + "rules": { + "type": "number", + "description": "Z coordinate of the asset's location." + } + } + } + } + }, + "supplier": { + "default": "Unknown", + "rules": { + "type": "string", + "description": "The supplier or manufacturer of the asset." + } + }, + "type": { + "default": "pump", + "rules": { + "type": "string", + "description": "A general classification of the asset tied to the specific software. This is not chosen from the asset dropdown menu." + } + }, + "subType": { + "default": "Centrifugal", + "rules": { + "type": "string", + "description": "A more specific classification within 'type'. For example, 'centrifugal' for a centrifugal pump." + } + }, + "model": { + "default": "Unknown", + "rules": { + "type": "string", + "description": "A user-defined or manufacturer-defined model identifier for the asset." + } + }, + "accuracy": { + "default": null, + "rules": { + "type": "number", + "nullable": true, + "description": "The accuracy of the machine or sensor, typically as a percentage or absolute value." + } + }, + "machineCurve": { + "default": { + "nq": { + "1": { + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 10, + 20, + 30, + 40, + 50 + ] + } + }, + "np": { + "1": { + "x": [ + 1, + 2, + 3, + 4, + 5 + ], + "y": [ + 10, + 20, + 30, + 40, + 50 + ] + } + } + }, + "rules": { + "type": "machineCurve", + "description": "All machine curves must have a 'nq' and 'np' curve. nq stands for the flow curve, np stands for the power curve. Together they form the efficiency curve." + } + } + }, + "mode": { + "current": { + "default": "auto", + "rules": { + "type": "enum", + "values": [ + { + "value": "auto", + "description": "Machine accepts setpoints from a parent controller and runs autonomously." + }, + { + "value": "virtualControl", + "description": "Controlled via GUI setpoints; ignores parent commands." + }, + { + "value": "fysicalControl", + "description": "Controlled via physical buttons or switches; ignores external automated commands." + }, + { + "value": "maintenance", + "description": "No active control from auto, virtual, or fysical sources." + } + ], + "description": "The operational mode of the machine." + } + }, + "allowedActions":{ + "default":{}, + "rules": { + "type": "object", + "schema":{ + "auto": { + "default": ["statusCheck", "execMovement", "execSequence", "emergencyStop"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Actions allowed in auto mode." + } + }, + "virtualControl": { + "default": ["statusCheck", "execMovement", "execSequence", "emergencyStop"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Actions allowed in virtualControl mode." + } + }, + "fysicalControl": { + "default": ["statusCheck", "emergencyStop"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Actions allowed in fysicalControl mode." + } + }, + "maintenance": { + "default": ["statusCheck"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Actions allowed in maintenance mode." + } + } + }, + "description": "Information about valid command sources recognized by the machine." + } + }, + "allowedSources":{ + "default": {}, + "rules": { + "type": "object", + "schema":{ + "auto": { + "default": ["parent", "GUI", "fysical"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Sources allowed in auto mode." + } + }, + "virtualControl": { + "default": ["GUI", "fysical"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Sources allowed in virtualControl mode." + } + }, + "fysicalControl": { + "default": ["fysical"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Sources allowed in fysicalControl mode." + } + } + }, + "description": "Information about valid command sources recognized by the machine." + } + } + }, + "source": { + "default": "parent", + "rules": { + "type": "enum", + "values": [ + { + "value": "parent", + "description": "Commands are received from a parent controller." + }, + { + "value": "GUI", + "description": "Commands are received from a graphical user interface." + }, + { + "value": "fysical", + "description": "Commands are received from physical buttons or switches." + } + ], + "description": "Information about valid command sources recognized by the machine." + } + }, + "sequences":{ + "default":{}, + "rules": { + "type": "object", + "schema": { + "startup": { + "default": ["starting","warmingup","operational"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Sequence of states for starting up the machine." + } + }, + "shutdown": { + "default": ["stopping","coolingdown","idle"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Sequence of states for shutting down the machine." + } + }, + "emergencystop": { + "default": ["emergencystop","off"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Sequence of states for an emergency stop." + } + }, + "boot": { + "default": ["idle","starting","warmingup","operational"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Sequence of states for booting up the machine." + } + } + } + }, + "description": "Predefined sequences of states for the machine." + + }, + "calculationMode": { + "default": "medium", + "rules": { + "type": "enum", + "values": [ + { + "value": "low", + "description": "Calculations run at fixed intervals (time-based)." + }, + { + "value": "medium", + "description": "Calculations run when new setpoints arrive or measured changes occur (event-driven)." + }, + { + "value": "high", + "description": "Calculations run on all event-driven info, including every movement." + } + ], + "description": "The frequency at which calculations are performed." + } + } + } + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..d38a86b --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "rotatingMachine", + "version": "1.0.3", + "description": "Control module rotatingMachine", + "main": "rotatingMachine.js", + "scripts": { + "test": "node rotatingMachine.js" + }, + "repository": { + "type": "git", + "url": "https://gitea.centraal.wbd-rd.nl/RnD/rotatingMachine.git" + }, + "keywords": [ + "rotatingMachine", + "node-red" + ], + "author": "Rene De Ren", + "license": "SEE LICENSE", + "dependencies": { + "generalFunctions": "git+https://gitea.centraal.wbd-rd.nl/RnD/generalFunctions.git", + "convert": "git+https://gitea.centraal.wbd-rd.nl/RnD/convert.git" + }, + "node-red": { + "nodes": { + "rotatingMachine": "rotatingMachine.js" + } + } +} diff --git a/readme/Training_data b/readme/Training_data new file mode 100644 index 0000000..382a413 --- /dev/null +++ b/readme/Training_data @@ -0,0 +1,22 @@ +let ctrl0 = [0,0.4,1.4,2.4,3.4,4.4,5.4,6.4,7.4,8.4,9.4,10.4,11.6,12.6,13.8,14.8,15.8,16.8,17.8,18.8,19.8,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,51.5,52,53,54,55,56,57,58.2,59.2,60.2,61.2,62.2,63.2,64.2,65.2,66.2,67.2,68.2,69.2,70.2,71.2,72.2,73.2,74.2,75.2,76.2,77.4,78.6,79.8,80.8,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.4,67.4,65.4,63.4,61.4,59.4,57.4,55.4,53.4,51.4,49.4,48.3,47.2,45.2,43.2,41,39,37,35,33,31,29,27,25,23,21,19,17,15,13,11,9,7,5,3,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.2,31.4,32.4,33.4,34.4,35.4,36.4,37.4,38.4,39.4,40.4,41.4,42.4,43.6,44.2,44.8,45.8,46.8,47.8,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,84.8,82.8,80.8,78.8,76.6,74.6,72.6,70.6,68.6,66.6,64.6,62.4,60.4,58.4,56.4,54.4,53.3,52.2,50.2,48,46,44,41.8,39.8,37.8,35.8,33.6,31.6,29.6,27.6,25.6,23.6,21.6,19.6,17.6,15.6,13.6,11.6,9.6,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.6,5.8,6.8,7.8,8.8,9.8,10.8,11.8,12.8,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32.5,33,34,35,36.2,37.4,38.4,39.4,40.4,41.4,42.6,43.6,44.6,45.6,46.6,47.6,48.8,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,67.5,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,85,83,81,78.8,76.8,74.8,72.8,70.8,68.8,66.8,64.8,62.8,60.8,58.8,56.8,54.8,52.8,50.8,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.6,24.6,22.6,20.6,18.6,16.6,14.6,12.6,10.6,8.6,6.6,4.6,2.6,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21.2,22.4,23.4,24.4,25.4,26.4,27.4,28.4,29.4,30.4,31.4,32.4,33.6,34.2,34.8,35.8,36.8,37.8,38.8,39.8,40.8,41.8,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.2,67.2,65.2,63.2,61.2,59.2,57.2,55,53,51,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.8,24.8,22.8,20.8,18.8,16.8,14.8,12.8,10.8,8.6,6.6,4.6,2.6,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,33.6,31.6,29.6,27.6,25.6,23.6,21.6,19.6,17.6,15.6,13.6,11.6,9.6,7.6,5.6,3.6,2.5,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.6,79.6,80.6,81.6,82.2,82.8,83.8,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99]; +let ctrl1 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.2,31.4,32.4,33.4,34.4,35.4,36.4,37.4,38.4,39.4,40.4,41.4,42.4,43.6,44.2,44.8,45.8,46.8,47.8,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,84.8,82.8,80.8,78.8,76.6,74.6,72.6,70.6,68.6,66.6,64.6,62.4,60.4,58.4,56.4,54.4,53.3,52.2,50.2,48,46,44,41.8,39.8,37.8,35.8,33.6,31.6,29.6,27.6,25.6,23.6,21.6,19.6,17.6,15.6,13.6,11.6,9.6,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.6,5.8,6.8,7.8,8.8,9.8,10.8,11.8,12.8,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32.5,33,34,35,36.2,37.4,38.4,39.4,40.4,41.4,42.6,43.6,44.6,45.6,46.6,47.6,48.8,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,67.5,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,85,83,81,78.8,76.8,74.8,72.8,70.8,68.8,66.8,64.8,62.8,60.8,58.8,56.8,54.8,52.8,50.8,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.6,24.6,22.6,20.6,18.6,16.6,14.6,12.6,10.6,8.6,6.6,4.6,2.6,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21.2,22.4,23.4,24.6,25.6,26.6,27.2,27.8,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.2,67.2,65.2,63.2,61.2,59.2,57.2,55,53,51,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.8,24.8,22.8,20.8,18.8,16.8,14.8,12.8,10.8,8.6,6.6,4.6,2.6,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,33.6,31.6,29.6,27.6,25.6,23.6,21.6,19.6,17.6,15.6,13.6,12.5,11.4,9.4,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.6,79.6,80.6,81.6,82.2,82.8,83.8,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99]; +let ctrl2 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.6,1.6,2.6,3.6,4.6,5.8,6.8,7.8,8.8,9.8,10.8,11.8,12.8,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32.5,33,34,35,36.2,37.4,38.4,39.4,40.4,41.4,42.6,43.6,44.6,45.6,46.6,47.6,48.8,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,67.5,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,85,83,81,78.8,76.8,74.8,72.8,70.8,68.8,66.8,64.8,62.8,60.8,58.8,56.8,54.8,52.8,50.8,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.6,24.6,22.6,20.6,18.6,16.6,14.6,12.6,10.6,8.6,6.6,4.6,2.6,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21.2,22.4,23.4,24.6,25.6,26.6,27.2,27.8,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.2,67.2,65.2,63.2,61.2,59.2,57.2,55,53,51,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.8,24.8,22.8,20.8,18.8,16.8,14.8,12.8,10.8,8.6,6.6,4.6,2.6,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,34.5,33.4,31.4,29.4,27.4,25.4,23.4,21.4,19.4,17.4,15.4,13.4,11.4,9.4,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.6,79.6,80.6,81.6,82.2,82.8,83.8,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99]; +let ctrl3 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21.2,22.4,23.4,24.6,25.6,26.6,27.2,27.8,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.2,67.2,65.2,63.2,61.2,59.2,57.2,55,53,51,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.8,24.8,22.8,20.8,18.8,16.8,14.8,12.8,10.8,8.6,6.6,4.6,2.6,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,34.5,33.4,31.4,29.4,27.4,25.4,23.4,21.4,19.4,17.4,15.4,13.4,11.4,9.4,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.6,79.6,80.6,81.6,82.2,82.8,83.8,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99]; +let ctrl4 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,34.5,33.4,31.4,29.4,27.4,25.4,23.4,21.4,19.4,17.4,15.4,13.4,11.4,9.4,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.4,79.4,80.4,81.4,82.6,83.6,84.6,85.6,86.6,87.6,88.6,89.6,90.6,92,92.6,93.6,94.6,95.6,96.2,96.8,97.8,98.8]; +let ctrl5 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.6,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.4,79.4,80.4,81.4,82.6,83.6,84.6,85.6,86.6,87.6,88.6,89.6,90.6,92,92.6,93.6,94.6,95.6,96.2,96.8,97.8,98.8]; + +let PT_1 = [85.9,87.4,90.5,93.4,96.8,99.9,103,107,110,114,118,121,125,129,133,137,140,143,147,150,153,155,157,159,161,162,163,165,166,168,169,170,171,172,174,174,176,177,178,179,181,181,182,184,184,185,186,187,188,189,190,191,193,194,195,196,196,197,199,199,201,201,202,203,204,206,206,207,208,210,210,211,213,214,215,215,217,218,219,219,219,219,219,219,219,219,219,219,219,219,219,219,218,219,218,218,218,218,219,219,219,219,218,219,219,218,218,218,218,219,220,218,216,215,212,210,208,206,204,202,200,198,197,194,193,190,189,186,184,182,180,178,175,173,171,168,166,163,159,156,152,147,142,134,127,119,112,104,97.2,92.1,92.2,95.2,98.6,102,105,109,112,116,119,123,127,131,135,138,142,144,147,150,152,154,156,159,160,162,164,165,167,168,169,170,172,173,174,175,177,178,179,180,181,182,183,184,185,184,185,186,187,188,189,190,191,192,193,195,195,197,198,198,199,201,202,203,204,205,206,207,208,208,210,211,212,212,213,215,216,217,217,217,217,218,218,217,217,218,217,217,217,217,217,217,217,217,217,217,218,217,217,217,217,218,218,217,217,217,217,217,218,217,217,216,215,212,210,209,206,204,202,200,198,197,194,191,190,187,186,184,182,179,178,175,173,170,168,165,163,160,156,152,148,143,137,130,123,115,107,101,93.4,90.2,92.1,95.1,98.7,102,106,109,113,116,121,124,129,132,135,139,142,144,147,150,152,154,156,157,159,161,163,164,166,167,169,170,171,172,173,175,176,177,179,180,181,181,182,184,184,185,186,187,188,189,190,191,192,193,194,195,196,197,199,200,200,202,203,204,205,206,207,208,209,210,209,210,211,212,214,215,216,216,217,217,216,217,217,217,217,216,217,217,217,217,217,217,217,217,218,217,217,218,218,217,218,217,217,217,217,217,217,217,217,217,217,215,213,211,209,207,204,203,201,198,196,194,192,190,189,187,185,183,181,179,177,175,172,170,167,165,162,158,155,150,145,140,132,126,118,110,102,96,90.3,90.9,93.8,96.4,100,103,107,111,115,118,122,125,130,133,136,138,141,144,146,148,151,153,155,157,158,160,161,163,165,166,168,169,170,171,173,173,175,176,178,178,179,181,181,183,183,184,185,186,187,187,188,188,190,190,192,192,194,194,195,196,197,198,200,201,202,203,204,205,206,208,209,209,210,211,213,214,211,207,206,206,205,206,206,206,206,206,206,206,205,206,205,206,206,205,205,205,205,205,206,206,205,206,205,206,205,205,206,205,206,205,204,202,200,197,195,193,191,189,186,184,182,180,178,175,174,172,171,172,177,175,173,171,169,167,164,161,157,154,150,146,141,134,128,121,113,106,99.4,93,90,92.4,95.7,98.7,102,105,109,112,116,120,124,128,131,134,136,139,141,143,145,147,149,151,153,155,156,158,160,161,162,164,166,166,168,169,171,172,173,174,175,177,177,177,178,179,180,180,181,182,184,184,185,186,187,189,189,190,191,191,193,194,195,194,188,187,189,190,191,192,193,195,196,197,198,200,200,202,203,204,205,206,205,205,205,205,206,205,205,205,205,205,205,205,205,206,205,205,205,205,205,206,206,205,204,205,205,204,205,205,204,203,201,198,195,193,191,189,186,184,182,178,176,175,173,170,169,167,164,163,160,159,155,156,161,159,156,154,150,147,143,138,133,128,121,115,108,102,95.3,89.5,89.8,92.3,95.5,98.9,102,106,110,114,117,120,124,127,130,133,135,136,138,139,140,142,144,146,148,150,151,153,154,155,157,159,160,162,163,164,166,167,167,168,169,170,171,173,174,175,175,177,178,178,179,180,181,181,183,178,173,176,177,177,179,181,182,183,184,186,186,188,189,190,191,192,193,195,196,197,198,199,201,201,202,203,203,203,203,202,203,204,204,203,204,203,204,203,203,203,204,204,204,154]; +let PT_2 = [47.9,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.7,47.6,47.6,47.5,47.5,47.4,47.3,47.2,47.2,47,46.9,46.8,46.5,46.4,46.2,46.1,45.9,45.6,45.5,45.4,45.3,45.1,44.9,44.6,44.6,44.3,44,43.8,43.5,43.7,43.4,43.2,43,42.7,42.4,42.4,42,41.9,41.6,41.6,41.2,41.3,40.7,40.6,40.4,40.3,40,39.5,39.3,39.2,38.8,38.5,38.3,37.7,37.8,37.4,37.3,36.7,36.5,36.4,35.8,35.5,35.2,35.2,34.9,35,34.8,34.9,35,35.1,35.3,34.9,35.1,34.9,35,34.8,35,34.8,34.7,34.8,34.8,34.9,34.7,35,34.9,34.9,56.9,80.8,81,81.1,81.6,81.5,81.3,81.5,81.7,81.4,81.9,82.2,82.8,83.3,84.2,84.6,85.3,85.7,86.1,86.7,87.3,87.7,88.1,88.6,88.9,89.5,89.9,90.3,90.5,91.1,91.2,91.5,92.3,92.7,93,93.4,93.8,93.9,94.1,94.2,94.5,94.7,94.8,94.8,94.9,94.9,94.9,95,96.8,99.9,103,106,110,114,117,121,124,128,132,136,140,144,148,152,153,158,161,163,165,167,169,170,172,174,176,178,178,181,182,184,185,186,186,189,190,191,192,193,195,195,196,197,199,200,201,202,203,204,206,207,208,209,209,210,212,213,214,215,216,217,218,219,220,222,223,223,225,227,227,229,230,230,232,233,234,236,236,237,237,237,238,237,237,237,237,237,237,237,237,237,237,237,236,237,236,237,236,236,236,236,236,237,236,237,236,236,237,236,234,231,229,226,224,222,220,218,215,213,211,209,207,204,202,200,198,196,194,191,189,186,184,180,177,174,171,168,164,160,155,148,141,133,125,117,110,103,99.8,102,105,109,112,116,120,123,127,132,135,140,143,147,150,154,157,160,162,164,166,168,170,171,173,175,176,178,179,181,183,184,185,186,188,189,191,192,192,194,195,197,198,198,200,201,202,203,204,205,206,207,208,210,210,212,212,214,215,216,217,218,220,221,223,222,224,225,227,227,228,228,229,230,231,233,234,235,236,236,236,237,236,236,235,237,236,236,236,237,236,236,237,236,236,235,236,236,236,236,236,237,235,236,236,236,235,235,235,234,231,229,226,224,222,220,217,215,213,210,209,207,205,203,201,199,196,195,192,190,187,184,182,179,176,172,169,165,162,157,151,143,136,127,120,112,105,100,100,104,107,110,114,118,122,125,129,134,138,141,144,147,151,153,156,159,162,164,167,169,169,170,172,173,175,176,178,180,181,182,184,186,186,188,189,191,192,194,194,195,197,197,198,200,201,201,202,202,203,205,206,207,208,209,211,211,212,214,215,216,217,218,219,220,222,222,223,225,227,227,227,230,231,227,223,224,224,224,225,225,225,224,225,225,225,225,225,224,224,225,225,224,225,225,224,225,224,224,223,224,225,225,224,224,225,224,223,221,218,216,213,211,209,206,203,201,199,197,194,192,190,188,186,183,186,190,187,185,183,180,178,175,172,169,166,161,157,151,145,138,131,122,114,107,102,98.2,100,104,107,111,114,118,122,126,130,134,138,141,144,146,148,151,152,154,157,160,163,165,166,168,171,174,176,179,180,181,178,180,182,183,184,186,187,188,189,190,190,191,192,193,194,196,197,197,199,199,201,202,203,204,205,205,207,208,209,209,208,203,202,204,205,206,207,208,210,211,213,213,214,216,217,219,219,221,222,222,223,224,224,223,224,224,224,224,223,223,222,222,223,222,223,223,223,223,223,224,224,224,222,223,223,223,222,222,219,217,213,211,208,205,203,200,198,195,194,191,189,186,185,183,180,177,175,173,171,167,168,174,172,169,166,163,159,154,149,144,139,132,126,118,111,104,98.4,98.6,102,105,109,112,116,120,124,127,131,135,138,140,143,145,147,149,150,150,153,155,157,160,161,164,168,169,171,173,174,176,178,179,181,183,184,183,184,184,184,185,186,188,188,189,190,191,193,193,195,195,196,196,193,187,191,191,192,194,196,196,198,200,200,202,203,205,206,207,208,210,212,213,213,215,217,218,220,220,221,221,223,222,224,224,223,224,224,223,223,224,223,223,224,224,223,223,189]; +let PT_3 = [47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.7,47.7,47.7,47.7,47.7,47.7,47.6,47.6,47.5,47.5,47.4,47.3,47.2,47.1,47,46.8,46.6,46.5,46.3,46.2,46.1,45.9,45.7,45.6,45.4,45.1,45,44.9,44.7,44.4,44.2,44.2,44,43.8,43.4,43.3,43.1,42.9,42.8,42.7,42.3,42.3,42.1,41.8,41.4,41.4,40.9,40.9,40.3,40.5,40.2,39.8,39.6,39.4,39.1,38.9,38.4,38.3,38.3,37.8,37.6,37.1,36.9,36.6,36.3,36.1,35.7,35.4,35.1,35.3,35.1,35,34.8,35.1,34.8,34.8,35,34.9,34.7,34.9,35,34.9,35.1,35.2,35.4,35.1,35.2,35,35.1,35,34.9,35.1,34.8,35,34.9,34.6,34.7,34.8,35,34.8,35,34.9,35.6,36.2,36.8,37.2,37.8,38.2,38.9,39.3,40,40.7,41,41.3,41.8,42.1,42.5,42.9,43.4,43.7,44.1,44.5,44.8,45.1,45.5,45.8,46.1,46.5,46.7,47,47.3,47.4,47.6,47.7,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.7,47.7,47.7,47.7,47.7,47.6,47.1,46.8,46.3,46,45.7,45.3,44.8,44.4,43.9,43.6,43,42.6,42,41.3,40.8,40.3,39.7,39,38.4,37.7,37.2,36.4,35.8,35.2,34.5,33.9,33,32.6,32.5,31.7,31.1,30.4,29.6,29,28.2,27.3,26.7,25.9,25.1,24.1,23.5,23,22,21.3,20,19.3,18.5,17.4,16.5,15.2,14.4,13.3,12.5,12.1,10.8,9.61,8.65,7.39,6.48,5.23,4.37,3.25,3.11,3.11,2.53,3.09,2.77,2.54,2.36,2.77,2.29,2.61,2.59,2.59,2.57,2.36,2.63,2.73,2.97,3.01,2.64,2.72,2.6,3.51,49,49.1,49.7,49.1,49.5,49.7,49.2,49.4,49.1,49.7,51,52.9,55.2,56.7,59.3,61,62.7,64.7,66.4,68.4,69.9,71.3,73.1,74.3,75.8,77.6,78.6,79.9,81.2,82.8,84,85.1,86.5,87.7,88.7,89.6,90.5,91.5,92.4,93,93.6,94,94.1,94.4,94.3,94.4,94.4,94.8,97.4,100,104,107,111,114,118,121,125,129,134,138,142,146,149,153,156,159,161,164,166,168,170,172,173,175,176,178,179,181,182,183,185,186,187,189,190,191,192,193,194,195,196,197,199,200,201,202,204,204,205,206,207,208,210,210,212,213,214,215,215,217,218,219,221,221,223,224,224,224,225,226,227,227,229,230,231,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,231,232,231,231,232,232,233,232,232,232,232,231,232,231,229,227,224,223,221,219,215,214,212,209,208,206,203,201,200,198,196,194,191,189,186,184,181,178,176,173,169,166,161,156,149,142,134,126,118,111,105,98.8,98.9,102,106,109,113,116,120,123,127,131,135,139,143,146,149,152,155,158,160,162,164,166,168,170,171,173,175,176,178,179,180,182,183,185,186,187,188,190,190,192,193,194,196,196,197,199,200,201,201,201,202,204,205,206,206,207,209,210,211,213,214,214,216,217,218,219,221,221,223,224,224,226,226,229,229,226,222,222,222,222,221,222,222,222,222,221,222,222,223,222,221,221,222,222,221,222,222,222,222,222,221,222,221,222,222,221,221,221,222,219,217,214,212,209,208,205,202,200,198,196,194,191,190,187,185,183,186,189,187,185,183,180,178,175,172,168,165,161,156,150,143,137,128,122,114,108,101,98.1,100,104,107,111,114,118,121,125,130,133,137,140,143,146,149,151,153,155,158,160,162,164,166,167,169,170,172,174,176,177,178,180,182,182,184,185,187,187,189,190,190,191,191,193,194,195,196,197,198,199,200,202,203,203,205,205,206,207,208,210,208,204,201,205,206,207,209,210,210,213,215,215,216,217,219,219,221,222,223,222,224,222,222,223,222,223,222,222,222,223,223,222,222,222,222,223,223,222,223,222,223,223,222,222,222,223,223,221,220,217,215,213,210,208,204,202,199,197,195,192,190,188,185,184,181,179,177,174,173,169,170,176,173,170,167,164,159,154,149,144,137,131,123,117,110,104,98,98.3,101,104,108,111,115,119,123,126,130,133,136,139,141,144,147,148,150,151,153,155,158,160,163,164,167,168,169,171,173,174,176,177,178,179,181,181,182,183,184,186,186,188,188,190,191,191,193,194,195,196,197,197,192,187,191,191,194,194,196,196,199,199,200,202,203,205,207,208,209,211,211,214,214,216,218,219,220,220,222,223,225,224,226,224,224,223,224,224,223,224,224,225,224,223,224,224,171]; +let PT_4 = [104,104,105,105,105,105,105,104,104,105,104,104,105,105,104,104,104,105,104,103,104,104,104,104,104,103,104,104,103,103,103,103,102,102,102,102,102,102,102,101,101,102,100,99.3,100,101,99.9,99.4,100,99.5,98.9,99.5,99.4,98.9,97.6,98.2,98.1,97.6,96.7,97,97.5,96.2,95.8,96.3,95.9,94.8,96,95,94.2,94,94.4,94.1,93.7,93.1,92.3,91.7,93.2,92,91.3,91.9,92.3,91,91.1,93,90.4,91.2,92.4,90.6,91.5,91.4,90.1,92,92.6,90.8,91,92.9,91.7,91.1,90.5,91.6,92.4,91.2,91.4,91.8,92.2,90.9,91.7,92.8,91.7,90.5,92.4,91.5,91.3,92.6,94.7,94.4,94.3,95.2,96.5,97,96,97,98.1,97.5,98.6,99.6,98.8,100,101,101,101,100,101,103,103,102,103,103,103,103,104,105,104,104,106,105,104,105,105,104,105,105,104,105,105,104,105,106,104,104,106,105,104,103,105,104,103,103,103,102,102,101,101,101,100,99.7,98.6,99.4,98.8,97.5,95.9,96.8,96.2,95.5,94.5,92.9,92.7,93.3,92.3,90,89.3,90.1,89.7,88.3,87.5,87.4,87.3,86.7,84.9,83.4,84.4,83.5,81.7,81.7,81.1,79.1,79,79.1,77.5,76.3,76.5,74.9,73.2,73.4,71.4,69.7,70.3,69.8,67.6,66.1,66.2,65.8,63.2,62.6,60.8,61.2,59.1,58.4,60.1,59,58.3,59,59.3,59.5,58.1,58.8,58.8,59.5,58.5,58.5,59.4,58.8,58.6,59.8,59.6,58.5,57.8,59.4,58.6,58.4,59.1,60.5,58.1,58.4,58.9,58.8,58.6,59,57.6,60.9,63.2,64.3,66.5,69.8,71.2,72.8,74.1,76.8,79.2,79.1,80.7,82.8,85.6,85.7,86.6,89.6,90.9,90.8,92.3,94.3,95.5,95.5,98.3,99.3,99.6,101,103,102,103,103,105,105,104,104,105,104,104,104,104,105,106,104,105,105,104,104,105,105,104,104,105,104,102,102,103,102,101,99.7,98.5,98.7,97.9,95.4,94.8,94.1,93.4,92.3,91.1,89.9,87,87.7,86.5,84.6,82.8,82.2,80.8,79.8,78.3,76.4,76.8,75.3,73,71.6,72,68.1,66.5,67,65.2,63.1,61.3,58.9,57.7,58.1,55,53.6,52.9,50,47.6,45.6,45.2,43,40.1,40,37,34.7,32.6,31.6,29.5,27.2,24.4,22.1,21,17.9,15.3,13.5,13.6,12.9,11,11.3,12.6,13.9,12.5,11.6,13.4,12.6,12.8,13.1,13.2,12.9,11.9,13,13.5,11.6,11.9,13.3,13.2,12.1,12.6,13,14,12.4,11,12.7,13.1,13,12.5,13,15.1,18.3,21.8,26.3,31.3,36.2,38.9,42,46.8,49.7,52.6,56.5,59.3,61.3,64.8,69.4,71.7,73.8,77.5,80.7,81.5,84.2,87.4,89.8,91.8,94.1,96.9,97.8,99,102,103,104,104,104,105,104,104,103,107,112,111,121,126,132,135,137,144,149,153,156,158,154,151,153,154,155,156,156,157,157,158,160,160,161,162,162,162,163,163,163,165,165,166,166,167,168,168,169,170,170,170,172,172,173,174,175,174,175,175,176,176,177,178,178,178,179,179,179,180,181,181,182,181,182,183,184,183,185,184,185,186,185,186,186,183,178,178,178,179,180,183,182,182,182,182,181,183,182,183,182,182,182,183,181,181,182,182,182,182,182,181,181,182,182,181,180,179,176,175,175,173,172,171,170,169,168,167,166,164,164,162,160,159,157,156,159,162,160,159,156,154,152,149,151,148,146,142,143,141,137,134,132,129,124,117,110,106,109,114,118,122,127,132,136,142,146,151,156,159,162,164,165,164,155,156,157,159,161,160,161,162,163,163,163,163,165,165,166,166,167,168,168,168,170,170,170,171,170,170,171,171,176,174,174,176,177,175,176,177,178,178,179,179,179,181,179,181,178,173,169,172,172,172,174,174,174,176,176,175,177,178,179,180,179,182,182,183,185,185,186,187,187,187,187,188,188,188,188,188,187,187,188,188,188,189,188,188,187,187,187,188,187,186,184,182,180,179,177,176,173,173,172,171,170,168,166,165,163,163,160,158,154,153,151,149,147,144,145,154,151,148,147,145,145,142,140,137,134,131,129,124,119,110,102,102,105,110,113,118,123,128,133,138,142,148,151,156,159,162,163,166,162,156,157,157,157,156,158,159,161,161,163,164,165,166,166,166,167,168,169,168,168,169,170,171,171,172,172,173,174,174,174,175,176,176,177,175,171,167,170,170,171,171,171,172,172,171,172,175,175,177,177,178,177,178,179,179,181,181,181,183,183,185,183,186,184,189,186,188,190,189,191,192,192,192,193,192,192,191,191,192,176]; +let PT_5 = [46.7,46.7,46.7,46.7,46.7,46.7,46.7,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.5,46.5,46.5,46.4,46.4,46.3,46.1,46,46,45.8,45.7,45.6,45.4,45.3,45.1,44.9,44.7,44.6,44.5,44.3,44,43.9,43.8,43.6,43.2,43.2,43.1,42.8,42.6,42.4,42.1,42,41.9,41.7,41.4,41.3,40.9,40.8,40.6,40.5,39.9,40.1,39.5,39.4,39.2,39.1,38.7,38.6,38.2,38,37.9,37.5,37.2,37.1,36.7,36.4,36,35.7,35.5,34.9,35.1,34.6,34.2,33.9,33.7,33.9,33.7,33.9,33.8,34.1,34.1,33.9,33.9,33.9,33.8,33.8,33.8,34.1,33.7,34.2,33.8,33.9,34,33.8,33.8,33.7,33.7,33.7,33.9,33.8,33.7,33.7,33.8,33.7,33.8,33.7,33.9,34.5,34.7,35.5,36.1,36.7,37.3,37.7,38.2,38.9,39.2,39.9,40.3,40.4,40.9,41.4,41.8,42.3,42.6,43,43.4,43.6,43.9,44.4,44.7,44.8,45.1,45.6,45.8,46.1,46.3,46.5,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.5,46.5,46.5,46.5,46.5,46.5,46.5,46.5,46.4,46,45.6,45.2,44.8,44.5,44.1,43.8,43.3,42.8,42.3,41.9,41.3,40.8,40.1,39.9,39.2,38.6,37.9,37.2,36.5,35.9,35.3,34.7,34.1,33.3,32.6,32.2,31.3,31.5,30.6,29.9,29.2,28.4,27.9,27.2,26.4,25.5,24.9,24,23.3,22.4,21.8,21.1,20.2,18.9,18.3,17.2,16.4,15.7,14.3,13.6,12.3,11.3,10.9,9.45,8.51,7.33,6.51,5.29,4.28,3.36,2.06,2.07,2.11,1.58,2.35,1.93,2,1.24,1.62,1.38,1.4,1.56,1.43,1.61,1.32,1.59,2.01,1.79,1.88,1.65,1.61,1.68,1.53,1.61,1.56,1.92,1.15,1.89,1.8,1.51,1.88,1.63,2.33,2.96,5.33,7.41,9.19,11.3,13.5,15.3,17.2,18.8,20.7,22.5,24,25.6,26.9,28.3,30,31.1,32.4,33.7,35.1,36.2,37.5,38.6,40,40.9,41.8,42.9,43.7,44.6,45.1,45.9,46.3,46.6,46.6,46.7,46.6,46.6,46.6,46.7,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.5,46.5,46.4,46.4,45.9,45.4,44.9,44.3,43.6,42.8,42,41.2,40.3,39.4,38.5,37.6,36.4,35.4,34.3,33,32,30.7,29.4,28.2,27,26,24.7,23.6,22.2,20.9,19.6,18.5,17.2,15.5,14.3,13,11.3,10.3,8.34,7.2,5.62,4.49,2.5,1.41,0.382,0.025,0.0282,0.0315,0.0347,0.0379,0.0411,0.0444,0.0476,0.0508,0.054,0.0573,0.0605,0.0637,0.067,0.0702,0.0734,0.0766,0.0799,0.0831,0.0863,0.0895,0.0928,0.096,0.0992,0.102,0.106,0.109,0.112,0.115,0.119,0.122,0.125,0.128,0.132,0.135,0.138,0.141,0.144,0.148,0.151,0.154,0.157,0.161,0.164,0.167,0.17,0.173,0.177,0.18,0.183,0.186,0.19,0.193,0.196,0.199,0.203,0.206,0.209,0.212,0.215,0.219,0.222,0.225,0.228,0.232,0.235,1.98,5.13,8.15,11.1,14,16.5,19.4,22,24.6,27.1,29.4,31.9,34.1,36.2,38.3,40,42,43.3,45,45.8,46.2,46.6,46.6,46.6,46.6,46.6,46.7,46.7,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.5,46.3,45,43.9,42.9,41.9,40.7,39.7,38,36.6,35.1,33.4,31.5,29.7,27.8,25.9,23.7,21.7,19.9,17.9,16.3,14.1,11.8,10,7.41,5.78,3.03,1.18,0.088,0.0904,0.0929,0.0953,0.0977,0.1,0.103,0.105,0.107,0.11,0.112,0.115,0.117,0.12,0.122,0.124,0.127,0.129,0.132,0.134,0.137,0.139,0.141,0.144,0.146,0.149,0.151,0.153,0.156,0.158,0.161,0.163,0.166,0.168,0.17,0.173,0.175,0.178,0.18,0.183,0.185,0.187,0.19,0.192,0.195,0.197,0.2,0.202,0.204,0.207,0.209,0.212,0.214,0.217,0.219,0.221,0.224,0.226,0.229,0.231,0.234,0.236,0.238,0.241,0.243,0.246,0.248,0.25,0.253,0.255,0.258,0.26,0.263,0.265,0.267,0.27,0.272,0.275,0.277,0.28,4.98,9.21,14.5,20.3,25.4,31.4,35.3,40.9,46.1,54.4,58.7,64.2,68.1,73.1,76.9,81.1,85.3,88.7,91.9,96,98.8,101,103,104,105,106,106,106,107,110,113,117,121,125,129,133,138,142,147,152,157,154,158,159,161,162,163,164,165,166,167,168,169,170,170,171,172,173,174,174,175,176,177,177,178,178,179,180,180,181,180,181,182,183,183,184,185,186,186,187,188,188,189,188,190,191,191,191,192,193,191,184,182,184,184,184,185,186,186,188,187,188,190,189,190,191,191,193,194,195,196,195,193,193,196,193,194,194,195,193,194,194,194,194,193,193,194,193,193,193,193,193,193,193,192,191,190,191,188,185,182,183,181,180,179,178,177,176,175,173,172,171,170,169,168,166,165,164,163,160,163,171,171,170,169,167,165,164,161,159,157,152,144,136,128,120,112,113,116,120,124,128,133,137,143,147,151,153,154,157,159,160,161,162,163,164,165,166,167,168,169,169,170,171,172,173,173,174,175,176,177,177,177,177,178,179,179,180,180,181,182,183,184,184,185,186,187,187,188,188,183,177,180,181,181,182,183,184,184,185,186,186,186,188,188,189,190,190,191,192,192,193,193,194,196,197,198,199,199,199,201,203,203,205,204,203,204,204,204,203,204,204,204,204,173]; +let PT_6 = [40.7,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.5,40.4,40.4,40.3,40.2,40.2,40.1,39.9,39.8,39.6,39.5,39.4,39.2,39.1,39,38.8,38.6,38.3,38.2,38,37.9,37.7,37.5,37.4,37.1,36.9,36.6,36.5,36.4,36.2,36,35.7,35.5,35.4,35.1,35,35,34.6,34.1,34.2,33.7,33.6,33.3,33.3,33,32.8,32.3,32.2,31.9,31.4,31.3,31,30.5,30.3,30,30,29.5,29.4,29.3,28.8,28.5,28,27.9,27.9,27.7,27.6,27.5,27.6,27.3,27.8,27.6,27.5,27.5,27.8,27.4,27.6,27.5,27.5,27.4,27.6,27.6,27.5,27.8,27.6,27.5,27.6,27.6,27.5,27.5,27.8,27.7,27.6,27.8,27.5,27.6,27.8,28.4,28.8,29.5,30.1,30.5,30.9,31.6,32.2,32.5,33.3,33.7,34.1,34.6,35,35.5,35.9,36.2,36.5,36.8,37.2,37.6,38.2,38.3,38.7,39.1,39.3,39.6,39.8,40.1,40.3,40.4,40.6,40.6,40.7,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.5,40.5,40.5,40.5,40.4,40.4,40.4,40.4,40,39.5,39.2,38.8,38.4,38.1,37.6,37.1,36.7,36.4,35.9,35.3,34.7,34.2,33.6,33,32.5,31.7,31.1,30.6,30,29.1,28.5,28,27.4,26.6,25.9,25.2,25.1,24.5,23.8,23,22.3,21.9,20.8,19.9,19.5,18.6,17.9,17.1,16,15.3,14.8,13.9,12.8,11.8,11.1,10.1,9.26,7.9,7.08,6.04,5.08,4.58,3.56,2.48,0.97,0.201,0.063,0.0668,0.0706,0.0744,0.0782,0.0819,0.0857,0.0895,0.0933,0.0971,0.101,0.105,0.108,0.112,0.116,0.12,0.124,0.127,0.131,0.135,0.139,0.143,0.146,0.15,0.154,0.158,0.162,0.165,0.169,0.173,0.177,0.18,0.184,0.188,0.192,0.196,0.199,0.203,1.21,2.65,5.08,6.68,8.61,10.8,12.6,14.5,16,17.5,19.3,20.7,22.3,23.6,25,26.1,27.5,28.8,30,31.2,32.6,33.8,34.8,35.8,36.9,37.6,38.5,39.2,39.9,40.3,40.4,40.6,40.5,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.5,40.5,40.5,40.4,39.8,39.3,38.9,38.2,37.6,36.8,36,35,34.1,33.3,32.4,31.2,30.2,29.3,28.2,26.8,25.7,24.6,23.4,22.1,20.8,19.6,18.3,17.4,15.9,14.6,13.3,12.3,10.5,9.36,7.87,6.53,4.95,3.66,2.13,0.752,0.088,0.0901,0.0921,0.0942,0.0962,0.0983,0.1,0.102,0.104,0.106,0.109,0.111,0.113,0.115,0.117,0.119,0.121,0.123,0.125,0.127,0.129,0.131,0.133,0.135,0.137,0.139,0.141,0.143,0.146,0.148,0.15,0.152,0.154,0.156,0.158,0.16,0.162,0.164,0.166,0.168,0.17,0.172,0.174,0.176,0.178,0.18,0.183,0.185,0.187,0.189,0.191,0.193,0.195,0.197,0.199,0.201,0.203,0.205,0.207,0.209,0.211,0.213,0.215,0.217,0.22,0.222,0.224,0.226,0.228,0.23,0.232,0.234,0.236,1.58,4.62,7.89,10.5,13.2,15.6,18.4,20.7,23.4,25.6,28,30.1,32.2,33.9,35.9,37.3,38.7,39.7,40.3,40.6,40.6,40.6,40.6,40.5,40.5,40.5,40.5,40.5,40.5,40.5,40.5,40.5,40.6,40.5,40.5,40.3,40.2,39,37.8,36.9,35.8,34.5,33.4,31.9,30.4,28.8,27.2,25.3,23.5,21.5,19.5,17.4,15.6,13.7,11.5,10.1,7.57,5.54,3.41,1.2,0.038,0.0398,0.0415,0.0433,0.0451,0.0468,0.0486,0.0504,0.0521,0.0539,0.0557,0.0575,0.0592,0.061,0.0628,0.0645,0.0663,0.0681,0.0698,0.0716,0.0734,0.0751,0.0769,0.0787,0.0804,0.0822,0.084,0.0857,0.0875,0.0893,0.0911,0.0928,0.0946,0.0964,0.0981,0.0999,0.102,0.103,0.105,0.107,0.109,0.111,0.112,0.114,0.116,0.118,0.119,0.121,0.123,0.125,0.126,0.128,0.13,0.132,0.133,0.135,0.137,0.139,0.141,0.142,0.144,0.146,0.148,0.149,0.151,0.153,0.155,0.156,0.158,0.16,0.162,0.164,0.165,0.167,0.169,0.171,0.172,0.174,0.176,0.178,0.179,0.181,0.183,0.185,0.187,0.188,0.19,0.192,0.194,0.195,0.197,0.199,0.201,0.202,0.204,3.13,7.4,11.8,15.7,19.6,23.1,26.9,30,32.8,35.5,37.3,38.8,39.6,40.2,40.4,40.5,40.5,40.5,40.5,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.5,39.5,38.1,36.6,34.7,33,30.1,28.2,26.2,24.2,21.9,19.7,17,14.7,12.2,9.74,7.14,4.29,1.51,0.044,0.0451,0.0461,0.0472,0.0483,0.0494,0.0504,0.0515,0.0526,0.0537,0.0547,0.0558,0.0569,0.058,0.059,0.0601,0.0612,0.0623,0.0633,0.0644,0.0655,0.0666,0.0676,0.0687,0.0698,0.0709,0.0719,0.073,0.0741,0.0752,0.0762,0.0773,0.0784,0.0795,0.0805,0.0816,0.0827,0.0838,0.0848,0.0859,0.087,0.0881,0.0891,0.0902,0.0913,0.0924,0.0934,0.0945,0.0956,0.0966,0.0977,0.0988,0.0999,0.101,0.102,0.103,0.104,0.105,0.106,0.107,0.108,0.11,0.111,0.112,0.113,0.114,0.115,0.116,0.117,0.118,0.119,0.12,0.121,0.122,0.124,0.125,0.126,0.127,0.128,0.129,0.13,0.131,0.132,0.133,0.134,0.135,0.136,0.137,0.139,0.14,0.141,0.142,0.143,0.144,5.14,11.5,17.4,24.3,29.9,36.9,42,50.6,61.2,66.7,72.5,78.1,82.8,87.5,90.9,94.8,97.6,101,103,104,105,106,106,108,111,115,120,124,128,133,138,142,145,148,150,152,154,155,156,155,157,158,159,160,162,162,164,164,165,167,168,168,169,170,171,172,172,173,174,175,174,175,176,177,178,179,179,181,181,182,182,183,183,185,186,186,186,181,175,179,179,180,180,181,182,183,183,183,183,185,186,186,186,187,188,188,190,190,191,192,194,195,196,197,196,200,199,202,203,203,203,202,203,203,201,201,204,203,203,202,202,154]; + +let FT_1 = [0.138,0.131,0.124,0.117,0.122,0.128,0.133,0.138,0.145,0.152,0.159,0.169,0.179,0.199,0.209,0.219,0.266,0.335,0.357,0.425,0.467,0.509,0.551,0.595,0.639,0.682,0.729,0.773,0.816,0.859,0.903,0.947,0.989,1.01,1.05,1.1,1.12,1.16,1.2,1.22,1.27,1.29,1.33,1.36,1.38,1.42,1.44,1.46,1.5,1.53,1.57,1.59,1.61,1.63,1.68,1.7,1.72,1.76,1.78,1.8,1.82,1.87,1.89,1.93,1.95,1.97,2.01,2.03,2.06,2.1,2.12,2.14,2.16,2.21,2.23,2.25,2.29,2.31,2.33,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.33,2.29,2.23,2.18,2.11,2.07,2.02,1.96,1.89,1.85,1.78,1.74,1.67,1.62,1.56,1.51,1.44,1.39,1.33,1.26,1.19,1.12,1.06,0.984,0.892,0.819,0.725,0.64,0.569,0.451,0.345,0.249,0.186,0.123,0.056,0.028,0.007,0.00988,0.0128,0.0156,0.0185,0.0214,0.0243,0.0271,0.105,0.117,0.128,0.152,0.163,0.173,0.195,0.238,0.284,0.326,0.371,0.44,0.487,0.532,0.577,0.62,0.641,0.686,0.73,0.773,0.793,0.836,0.879,0.92,0.964,0.984,1.03,1.07,1.09,1.13,1.15,1.2,1.22,1.24,1.28,1.3,1.35,1.35,1.36,1.37,1.39,1.43,1.45,1.47,1.5,1.54,1.56,1.58,1.6,1.62,1.67,1.69,1.71,1.75,1.77,1.79,1.81,1.85,1.88,1.9,1.94,1.96,1.98,2,2.05,2.07,2.09,2.13,2.15,2.17,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.17,2.17,2.17,2.17,2.17,2.13,2.06,2.01,1.95,1.9,1.84,1.79,1.74,1.67,1.63,1.58,1.52,1.47,1.4,1.36,1.3,1.25,1.18,1.13,1.07,0.998,0.903,0.834,0.771,0.699,0.629,0.558,0.464,0.368,0.268,0.198,0.123,0.028,0.007,0.00964,0.0123,0.0149,0.0175,0.0202,0.0228,0.0255,0.0281,0.0307,0.0334,0.063,0.09,0.0987,0.107,0.116,0.19,0.21,0.252,0.295,0.339,0.404,0.449,0.472,0.515,0.56,0.606,0.627,0.671,0.692,0.736,0.779,0.799,0.844,0.885,0.906,0.951,0.992,1.01,1.04,1.08,1.1,1.12,1.17,1.19,1.21,1.25,1.27,1.29,1.31,1.35,1.38,1.4,1.42,1.44,1.46,1.5,1.53,1.55,1.57,1.59,1.61,1.63,1.67,1.69,1.71,1.73,1.75,1.78,1.82,1.84,1.86,1.88,1.91,1.95,1.97,1.99,2.03,2.05,2.07,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.07,2.07,2.03,1.99,1.92,1.87,1.83,1.76,1.72,1.68,1.61,1.57,1.52,1.47,1.43,1.36,1.32,1.25,1.2,1.16,1.09,1.03,0.961,0.919,0.851,0.76,0.688,0.623,0.553,0.48,0.381,0.285,0.21,0.135,0.034,0.01,0.0116,0.0133,0.0149,0.0166,0.0182,0.0199,0.0215,0.0232,0.0248,0.0265,0.0281,0.0298,0.0314,0.0331,0.0347,0.0364,0.116,0.181,0.226,0.247,0.291,0.335,0.377,0.421,0.442,0.486,0.529,0.55,0.593,0.614,0.636,0.677,0.699,0.721,0.763,0.784,0.826,0.848,0.89,0.911,0.932,0.975,0.996,1.02,1.04,1.06,1.08,1.1,1.12,1.15,1.19,1.21,1.23,1.25,1.27,1.29,1.32,1.34,1.36,1.38,1.4,1.44,1.46,1.49,1.51,1.53,1.55,1.59,1.61,1.63,1.66,1.68,1.7,1.74,1.76,1.78,1.8,1.82,1.87,1.89,1.91,1.91,1.91,1.91,1.91,1.9,1.9,1.9,1.9,1.9,1.9,1.9,1.9,1.9,1.89,1.89,1.89,1.89,1.89,1.89,1.89,1.89,1.89,1.9,1.9,1.9,1.9,1.9,1.9,1.91,1.91,1.91,1.9,1.87,1.82,1.78,1.74,1.67,1.63,1.58,1.53,1.49,1.45,1.4,1.36,1.29,1.27,1.2,1.18,1.12,1.03,0.983,0.937,0.87,0.802,0.74,0.67,0.623,0.556,0.491,0.418,0.342,0.274,0.21,0.142,0.043,0.019,0.0208,0.0226,0.0245,0.0263,0.0281,0.0299,0.0318,0.0336,0.0354,0.0372,0.0391,0.0409,0.0427,0.0445,0.0464,0.0482,0.073,0.148,0.191,0.212,0.233,0.256,0.278,0.323,0.345,0.389,0.41,0.432,0.474,0.518,0.539,0.56,0.581,0.625,0.636,0.668,0.679,0.711,0.732,0.754,0.776,0.797,0.819,0.841,0.862,0.904,0.915,0.926,0.947,0.968,0.989,1.01,1.03,1.05,1.07,1.09,1.14,1.16,1.18,1.2,1.22,1.24,1.26,1.3,1.32,1.37,1.41,1.42,1.45,1.47,1.49,1.51,1.54,1.56,1.6,1.62,1.64,1.65,1.66,1.68,1.7,1.72,1.72,1.71,1.71,1.7,1.69,1.68,1.68,1.68,1.68,1.68,1.68,1.68,1.67,1.67,1.67,1.67,1.67,1.67,1.67,1.67,1.67,1.67,1.66,1.66,1.66,1.66,1.66,1.67,1.68,1.7,1.69,1.68,1.64,1.59,1.55,1.5,1.45,1.41,1.37,1.29,1.25,1.18,1.13,1.09,1.05,1.03,0.979,0.938,0.891,0.848,0.801,0.757,0.694,0.606,0.537,0.491,0.421,0.375,0.305,0.26,0.213,0.191,0.113,0.021,0.0000000000000216,0.0015,0.003,0.0045,0.006,0.0075,0.009,0.0105,0.012,0.0135,0.015,0.0165,0.018,0.0195,0.021,0.0225,0.024,0.0255,0.027,0.0285,0.108,0.156,0.164,0.171,0.179,0.2,0.221,0.263,0.284,0.305,0.326,0.347,0.367,0.408,0.43,0.45,0.471,0.515,0.537,0.558,0.58,0.602,0.623,0.644,0.664,0.685,0.706,0.727,0.738,0.748,0.79,0.801,0.812,0.834,0.855,0.875,0.886,0.896,0.941,0.962,0.982,1,1.03,1.05,1.09,1.11,1.13,1.15,1.17,1.2,1.22,1.26,1.28,1.3,1.32,1.34,1.36,1.38,1.41,1.45,1.47,1.49,1.49,1.49,1.48,1.48,1.48,1.47,1.47,1.47,1.46,1.45,1.44,1.44,1.44,1.43,1.43,1.43,1.43,1.42,1.42,1.42,1.41]; +let FT_2 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.228,0.33,0.395,0.441,0.488,0.533,0.577,0.619,0.664,0.687,0.729,0.751,0.792,0.814,0.859,0.9,0.922,0.966,0.986,1.03,1.05,1.09,1.11,1.14,1.18,1.2,1.22,1.24,1.29,1.31,1.33,1.35,1.37,1.42,1.44,1.46,1.48,1.5,1.54,1.57,1.59,1.61,1.65,1.67,1.69,1.72,1.74,1.78,1.8,1.82,1.86,1.88,1.91,1.93,1.95,1.99,2.01,2.03,2.05,2.07,2.12,2.14,2.16,2.17,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.16,2.16,2.16,2.16,2.16,2.16,2.16,2.16,2.14,2.09,2.05,2,1.93,1.89,1.84,1.8,1.73,1.69,1.62,1.57,1.53,1.49,1.42,1.37,1.33,1.28,1.22,1.17,1.1,1.05,0.984,0.917,0.85,0.784,0.713,0.641,0.54,0.469,0.371,0.279,0.19,0.094,0.069,0.018,0.0204,0.0227,0.0251,0.0275,0.0298,0.0322,0.0345,0.0369,0.0393,0.0416,0.071,0.0768,0.0825,0.0883,0.094,0.164,0.206,0.252,0.322,0.369,0.415,0.458,0.501,0.546,0.568,0.613,0.654,0.677,0.719,0.741,0.784,0.805,0.827,0.869,0.889,0.91,0.931,0.975,0.996,1.02,1.06,1.08,1.12,1.15,1.19,1.21,1.23,1.25,1.29,1.32,1.34,1.36,1.38,1.4,1.44,1.46,1.49,1.51,1.53,1.55,1.57,1.61,1.63,1.65,1.67,1.7,1.72,1.74,1.78,1.8,1.82,1.84,1.86,1.89,1.91,1.95,1.97,1.99,2.02,2.04,2.06,2.08,2.08,2.08,2.08,2.08,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.01,1.97,1.92,1.88,1.83,1.79,1.72,1.67,1.63,1.58,1.52,1.47,1.43,1.38,1.32,1.28,1.23,1.17,1.1,1.05,0.982,0.936,0.869,0.826,0.762,0.694,0.623,0.548,0.479,0.386,0.311,0.192,0.123,0.047,0.018,0.0198,0.0216,0.0234,0.0253,0.0271,0.0289,0.0307,0.0325,0.0343,0.0361,0.0379,0.0398,0.0416,0.0434,0.0452,0.047,0.12,0.185,0.23,0.274,0.316,0.337,0.38,0.4,0.441,0.486,0.53,0.552,0.596,0.618,0.66,0.683,0.704,0.749,0.771,0.793,0.815,0.836,0.878,0.899,0.921,0.942,0.963,0.985,1.01,1.03,1.05,1.07,1.11,1.13,1.16,1.18,1.2,1.22,1.26,1.28,1.3,1.32,1.35,1.37,1.39,1.41,1.43,1.47,1.5,1.52,1.54,1.56,1.58,1.6,1.62,1.64,1.69,1.71,1.73,1.75,1.77,1.79,1.84,1.86,1.88,1.9,1.91,1.92,1.92,1.92,1.92,1.92,1.92,1.92,1.92,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.9,1.9,1.9,1.9,1.9,1.9,1.9,1.88,1.84,1.79,1.74,1.7,1.66,1.62,1.57,1.52,1.45,1.41,1.37,1.32,1.28,1.23,1.19,1.14,1.08,1.01,0.962,0.917,0.872,0.805,0.76,0.695,0.625,0.555,0.49,0.443,0.375,0.281,0.186,0.116,0.089,0.045,0.017,0.0184,0.0198,0.0212,0.0226,0.0241,0.0255,0.0269,0.0283,0.0297,0.0311,0.0325,0.0339,0.0354,0.0368,0.0382,0.0396,0.11,0.134,0.155,0.175,0.219,0.288,0.3,0.357,0.4,0.421,0.465,0.472,0.479,0.486,0.507,0.528,0.549,0.617,0.66,0.682,0.702,0.724,0.746,0.767,0.788,0.809,0.853,0.874,0.894,0.917,0.938,0.959,0.98,0.991,1.02,1.03,1.04,1.06,1.09,1.11,1.12,1.13,1.15,1.17,1.19,1.21,1.22,1.23,1.27,1.32,1.33,1.34,1.35,1.36,1.38,1.4,1.44,1.47,1.49,1.53,1.55,1.57,1.59,1.62,1.64,1.65,1.66,1.67,1.68,1.67,1.66,1.66,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.64,1.64,1.64,1.64,1.64,1.64,1.64,1.64,1.64,1.65,1.65,1.65,1.65,1.66,1.68,1.66,1.64,1.59,1.55,1.48,1.44,1.39,1.35,1.3,1.24,1.2,1.18,1.14,1.11,1.07,1.05,1.01,0.983,0.941,0.92,0.876,0.833,0.79,0.747,0.649,0.576,0.51,0.465,0.396,0.349,0.306,0.211,0.144,0.099,0.073,0.044,0.017,0.0186,0.0202,0.0217,0.0233,0.0249,0.0265,0.0281,0.0296,0.0312,0.0328,0.0344,0.0359,0.0375,0.0391,0.0407,0.0423,0.0438,0.0454,0.101,0.144,0.166,0.176,0.186,0.206,0.229,0.251,0.261,0.295,0.363,0.385,0.427,0.448,0.469,0.49,0.5,0.511,0.532,0.554,0.565,0.599,0.62,0.642,0.686,0.696,0.727,0.737,0.748,0.769,0.791,0.812,0.833,0.844,0.855,0.875,0.895,0.916,0.937,0.957,0.999,1.01,1.02,1.04,1.06,1.08,1.11,1.13,1.15,1.17,1.19,1.21,1.23,1.28,1.3,1.32,1.34,1.36,1.38,1.4,1.42,1.44,1.47,1.47,1.47,1.48,1.48,1.49,1.51,1.49,1.48,1.47,1.47,1.46,1.46,1.46,1.46,1.46,1.45,1.45,1.45,1.45,1.44]; +let FT_3 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.086,0.257,0.299,0.345,0.392,0.435,0.48,0.525,0.566,0.609,0.653,0.674,0.696,0.74,0.763,0.808,0.831,0.876,0.898,0.92,0.964,0.984,1,1.03,1.05,1.07,1.09,1.11,1.16,1.18,1.2,1.22,1.24,1.26,1.29,1.33,1.35,1.37,1.39,1.41,1.43,1.46,1.48,1.5,1.52,1.54,1.56,1.58,1.62,1.64,1.67,1.69,1.71,1.73,1.75,1.78,1.82,1.84,1.86,1.88,1.9,1.92,1.94,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.94,1.94,1.94,1.94,1.94,1.94,1.9,1.85,1.81,1.77,1.72,1.65,1.61,1.56,1.52,1.47,1.43,1.38,1.34,1.29,1.25,1.2,1.14,1.09,1.05,1,0.94,0.869,0.801,0.736,0.691,0.624,0.555,0.443,0.351,0.276,0.205,0.098,0.018,0.0193,0.0207,0.022,0.0233,0.0247,0.026,0.0273,0.0287,0.03,0.0313,0.0327,0.034,0.0353,0.0367,0.038,0.0393,0.0407,0.139,0.188,0.228,0.25,0.293,0.314,0.359,0.403,0.424,0.466,0.511,0.553,0.575,0.619,0.641,0.683,0.705,0.728,0.749,0.77,0.791,0.813,0.859,0.879,0.9,0.921,0.943,0.963,1.01,1.03,1.05,1.07,1.09,1.11,1.13,1.16,1.18,1.2,1.22,1.24,1.26,1.27,1.28,1.31,1.33,1.35,1.39,1.41,1.43,1.45,1.47,1.5,1.52,1.54,1.56,1.58,1.6,1.64,1.66,1.68,1.71,1.73,1.75,1.77,1.79,1.79,1.79,1.79,1.79,1.79,1.79,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.76,1.72,1.68,1.64,1.59,1.55,1.5,1.46,1.41,1.37,1.32,1.28,1.23,1.19,1.17,1.12,1.07,0.986,0.944,0.899,0.831,0.789,0.724,0.679,0.611,0.546,0.471,0.401,0.326,0.26,0.196,0.079,0.052,0.029,0.009,0.0104,0.0119,0.0133,0.0147,0.0162,0.0176,0.0191,0.0205,0.0219,0.0234,0.0248,0.0262,0.0277,0.0291,0.0306,0.032,0.125,0.193,0.213,0.223,0.234,0.277,0.299,0.32,0.343,0.386,0.408,0.451,0.474,0.517,0.54,0.585,0.607,0.628,0.65,0.671,0.692,0.735,0.756,0.777,0.798,0.82,0.83,0.841,0.882,0.903,0.913,0.924,0.946,0.966,0.988,1.01,1.03,1.05,1.06,1.07,1.09,1.12,1.14,1.16,1.18,1.2,1.22,1.24,1.29,1.33,1.34,1.37,1.39,1.41,1.43,1.46,1.48,1.5,1.52,1.54,1.56,1.58,1.6,1.63,1.64,1.65,1.64,1.64,1.63,1.63,1.62,1.6,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.62,1.62,1.62,1.62,1.62,1.62,1.62,1.62,1.62,1.62,1.63,1.62,1.61,1.6,1.56,1.51,1.47,1.43,1.38,1.34,1.29,1.25,1.21,1.17,1.12,1.1,1.06,1.03,0.989,0.944,0.901,0.881,0.835,0.792,0.749,0.651,0.589,0.525,0.453,0.409,0.34,0.265,0.222,0.179,0.074,0.0615,0.00000000000000455,0.00105,0.0021,0.00315,0.0042,0.00525,0.0063,0.00735,0.0084,0.00945,0.0105,0.0116,0.0126,0.0137,0.0147,0.0158,0.0168,0.0179,0.0189,0.02,0.116,0.159,0.182,0.192,0.202,0.244,0.265,0.285,0.306,0.328,0.349,0.37,0.413,0.424,0.434,0.454,0.476,0.518,0.54,0.561,0.604,0.626,0.647,0.669,0.69,0.711,0.733,0.754,0.775,0.796,0.817,0.828,0.838,0.86,0.88,0.89,0.9,0.921,0.944,0.964,0.985,1.01,1.03,1.05,1.07,1.09,1.11,1.13,1.16,1.18,1.2,1.22,1.24,1.26,1.3,1.33,1.35,1.37,1.39,1.41,1.43,1.45,1.46,1.46,1.47,1.48,1.49,1.5,1.49,1.48,1.45,1.45,1.45,1.45,1.45,1.44,1.44,1.44,1.44,1.43,1.43,1.43,1.38]; +let FT_4 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0235,0.0236,0.0236,0.0237,0.0237,0.0238,0.0238,0.0239,0.0239,0.125,0.138,0.15,0.248,0.325,0.525,0.734,0.803,0.847,0.892,0.958,1.03,1.07,1.11,1.16,1.18,1.22,1.26,1.31,1.35,1.39,1.42,1.46,1.47,1.48,1.5,1.55,1.57,1.61,1.63,1.67,1.72,1.74,1.78,1.8,1.85,1.87,1.89,1.94,1.96,2,2.04,2.07,2.09,2.13,2.15,2.2,2.22,2.26,2.3,2.31,2.32,2.32,2.34,2.36,2.37,2.39,2.4,2.41,2.43,2.44,2.45,2.46,2.47,2.5,2.51,2.52,2.54,2.56,2.6,2.64,2.69,2.73,2.8,2.84,2.89,2.93,2.95,2.95,2.94,2.93,2.93,2.93,2.93,2.93,2.93,2.93,2.93,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.91,2.91,2.91,2.84,2.75,2.65,2.6,2.55,2.53,2.51,2.47,2.44,2.42,2.38,2.36,2.34,2.3,2.25,2.2,2.14,2.07,2,1.96,1.91,1.81,1.75,1.7,1.63,1.58,1.51,1.46,1.37,1.32,1.26,1.19,1.12,1.01,0.938,0.832,0.678,0.549,0.4,0.259,0.133,0.019,0.0215,0.024,0.0265,0.029,0.0315,0.034,0.0365,0.039,0.0415,0.066,0.11,0.178,0.244,0.318,0.43,0.554,0.786,0.854,0.895,0.917,0.939,0.985,1.03,1.05,1.09,1.14,1.18,1.22,1.24,1.29,1.31,1.33,1.37,1.39,1.44,1.46,1.48,1.52,1.54,1.58,1.6,1.65,1.67,1.71,1.72,1.73,1.78,1.8,1.82,1.86,1.91,1.93,1.95,1.97,2.01,2.03,2.05,2.07,2.08,2.09,2.1,2.14,2.16,2.17,2.18,2.19,2.2,2.22,2.23,2.23,2.24,2.25,2.26,2.27,2.28,2.28,2.33,2.37,2.39,2.43,2.48,2.03,2.55,2.59,2.64,2.66,2.66,2.66,2.65,2.65,2.65,2.65,2.65,2.65,2.65,2.65,2.65,2.64,2.64,2.64,2.64,2.64,2.64,2.64,2.64,2.61,2.52,2.44,2.37,2.32,2.28,2.26,2.24,2.21,2.19,2.17,2.16,2.15,2.13,2.08,2.04,2,1.95,1.89,1.84,1.8,1.76,1.69,1.65,1.6,1.56,1.44,1.4,1.36,1.29,1.25,1.16,1.12,1.05,0.947,0.905,0.79,0.662,0.534,0.384,0.18,0.059,0.037,0.008,0.00983,0.0117,0.0135,0.0153,0.0172,0.019,0.0208,0.0227,0.0245,0.0263,0.0282,0.109,0.131,0.201,0.272,0.438,0.771,0.819,0.866,0.932,0.974,1.02,1.04,1.05,1.08,1.1,1.14,1.17,1.19,1.21,1.25,1.27,1.29,1.31,1.36,1.38,1.4,1.42,1.44,1.48,1.49,1.53,1.55,1.59,1.61,1.63,1.65,1.69,1.71,1.73,1.76,1.8,1.82,1.86,1.87,1.88,1.9,1.91,1.91,1.92,1.93,1.95,1.95,1.96,1.96,1.96,1.97,1.98,1.98,1.99,2,2,2.01,2.02,2.03,2.08,2.1,1.71,1.73,2.18,1.46,1.66,2.28,2.32,2.34,2.36,2.37,2.34,2.35,2.36,2.36,2.37,2.38,2.39,2.26,2.14]; +let FT_5 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.205,0.594,0.685,0.707,0.751,0.772,0.815,0.836,0.877,0.92,0.941,1,1.03,1.07,1.11,1.13,1.16,1.2,1.22,1.24,1.28,1.3,1.32,1.37,1.39,1.41,1.45,1.47,1.49,1.52,1.56,1.58,1.6,1.62,1.66,1.68,1.7,1.73,1.77,1.79,1.81,1.83,1.85,1.89,1.91,1.93,1.96,1.97,1.98,1.99,2.02,2.04,2.05,2.06,2.07,2.08,2.08,2.09,2.1,2.1,2.12,2.14,2.15,2.16,2.17,2.18,2.21,2.25,2.3,2.34,2.38,2.43,2.47,2.5,2.54,2.54,2.54,2.54,2.53,2.53,2.53,2.53,2.53,2.53,2.53,2.53,2.53,2.53,2.52,2.52,2.52,2.52,2.52,2.52,2.45,2.38,2.29,2.22,2.2,2.18,2.15,2.13,2.11,2.1,2.07,2.05,2.04,2.01,1.97,1.92,1.88,1.83,1.77,1.72,1.68,1.61,1.56,1.5,1.45,1.38,1.28,1.24,1.17,1.11,1.04,0.97,0.875,0.805,0.737,0.611,0.438,0.292,0.113,0.01,0.0125,0.015,0.0175,0.02,0.0225,0.025,0.0275,0.03,0.0325,0.035,0.0375,0.04,0.0425,0.227,0.509,0.637,0.679,0.7,0.743,0.765,0.776,0.81,0.831,0.873,0.893,0.935,0.956,0.978,1,1.04,1.06,1.08,1.11,1.13,1.15,1.19,1.21,1.24,1.26,1.28,1.32,1.34,1.36,1.39,1.41,1.43,1.45,1.47,1.49,1.53,1.56,1.58,1.6,1.62,1.64,1.68,1.7,1.73,1.75,1.77,1.78,1.79,1.8,1.8,1.81,1.81,1.82,1.83,1.84,1.85,1.85,1.86,1.87,1.88,1.89,1.89,1.9,1.91,1.93,1.94,1.98,2,2.02,2.07,2.12,2.16,2.18,2.2,2.25,2.24,2.23,2.23,2.22,2.21,2.21,2.2,2.19,2.18,2.18,2.17]; +let FT_6 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.072,0.0825,0.093,0.103,0.114,0.135,0.18,0.253,0.327,0.455,0.526,0.591,0.66,0.703,0.794,0.838,0.86,0.88,0.901,0.922,0.965,0.986,1.01,1.05,1.07,1.11,1.14,1.16,1.2,1.22,1.24,1.26,1.31,1.33,1.35,1.37,1.41,1.43,1.45,1.47,1.52,1.54,1.56,1.58,1.6,1.64,1.66,1.68,1.7,1.75,1.77,1.79,1.83,1.88,1.92,1.94,1.95,1.96,1.97,1.97,1.98,1.99,1.99,2,2.01,2.02,2.02,2.03,2.04,2.07,2.07,2.08,2.08,2.08,2.09,2.11,2.13,2.17,2.21,2.25,2.27,2.35,2.39,2.41,2.43,2.45,2.45,2.45,2.45,2.45,2.45,2.44,2.44,2.44,2.44,2.44,2.43]; + +let ET_1 = [8.19,7.63,7.63,8.37,8.34,9.13,9,9.22,9.93,9.67,10.7,10.7,11.3,11.9,12.1,12.4,13.3,14.8,15.8,16.6,17.5,18.4,20,21.3,22.6,23.8,24.9,26.6,28.1,29.4,31.2,32.4,34.1,35.5,37.1,38.6,40.6,42.5,44.5,45.9,48.1,50.2,52.4,54.3,56.1,57.7,60,62.7,64.8,66.4,68.4,71.5,73.7,76,78.5,81.2,83.3,86.8,89.2,91.9,94.4,103,107,110,114,117,121,125,129,134,136,140,145,149,153,157,162,169,172,172,173,174,175,175,174,175,174,175,174,174,174,173,174,174,174,175,175,174,175,174,175,174,176,174,175,174,175,174,175,174,175,174,167,159,151,141,134,126,119,113,105,98.3,92.2,83.2,76.9,72,67.3,62.6,58.8,54.6,51.2,46.8,43.4,39.3,35.7,32.7,29.8,26.9,24.3,21.7,18.9,16.9,14.3,12.6,11.2,10,9.24,9.08,8.08,7.55,7.96,7.84,7.97,8.83,9.03,9.44,9.65,10.1,10.4,11.1,10.9,11.5,12.6,13.2,14,15,16,17.2,18.1,19.2,20.4,21.6,22.8,24.5,25.2,27,28.2,29.6,31.6,33.1,33.9,35.9,37.8,39.1,41.1,42.6,44.3,46.4,48.8,50.6,51.8,54.2,56.2,56.8,58.7,61,62.8,64.5,67.1,69.4,71.4,74,76.8,78.8,81.4,84,86.9,90.2,93.4,95.7,105,108,111,114,118,122,125,130,133,137,141,144,149,153,158,163,165,166,167,165,167,166,167,167,167,166,166,167,167,165,166,165,166,167,167,166,166,166,167,167,167,166,166,166,166,166,167,166,167,165,158,149,140,132,125,117,111,104,97.3,91.2,80.9,76.2,70.9,67.1,61.9,58,53.8,49.8,46.5,43,39,36.2,33,29.6,26.3,24.6,21.8,19,17.4,15.2,13.2,12.4,10.3,9.61,8.81,8.53,8.03,7.77,8.03,7.8,8.27,8.6,8.79,9.07,9.58,9.87,10.5,10.4,10.4,11.3,12.7,13.1,14.4,14.9,15.5,17.2,18.2,19,19.7,21.5,22.8,23.8,24.8,26.5,27.8,29,30.4,32.1,33.7,35.2,36.8,38.5,40.6,42.1,44.1,45.6,47,49.5,51.1,52.7,55.1,57,58.5,60.9,62.8,65.2,67.7,70.1,72.3,74.6,76.9,79.6,81.1,84.4,87.2,90.2,92.9,96.7,105,108,112,115,118,121,125,130,133,136,141,145,149,154,158,163,164,164,165,162,164,164,164,164,165,164,164,164,164,164,164,164,164,165,164,165,164,164,165,164,165,165,165,164,165,165,164,164,165,158,149,141,134,125,118,112,105,98.4,91.9,82.1,77,73,68.1,62.7,59,54.9,50.9,47.7,43.6,39.8,36.7,33.6,30,27.4,25.1,22.5,19.9,17.8,15.9,14,12.1,11.1,9.94,9.04,8.5,8.09,7.71,8.18,8.18,8.4,8.47,8.75,9.35,8.97,9.38,9.96,10.2,10.3,10.8,11.2,11.9,12.7,14.1,14.9,15.7,16.3,17.2,18.5,19.4,20.8,21.6,22.7,24.2,25.7,27,27.9,29.6,30.8,32.7,34.2,35.4,37.1,38.9,40.5,42,44,45.5,47.5,49.2,51.2,53.1,54.7,56.7,58.3,60.3,62.5,64.5,67.2,69.3,71.1,73.5,76.9,79.5,81.5,82.4,85.8,89.6,92.1,93.5,104,107,111,114,117,120,125,128,133,137,141,145,149,154,159,161,160,161,161,160,160,160,160,161,160,160,161,160,161,160,160,160,160,159,160,160,161,160,160,160,159,160,160,161,160,160,161,159,147,140,132,125,118,111,104,97.1,86.4,81.2,75.7,70.9,66.4,62.3,58.3,54.6,50.5,46.6,43.2,39.5,36,32.7,30.1,26.8,24.5,22,21.3,17.5,15.7,14.3,12,11.9,10.3,9.4,9.11,8.63,7.98,7.6,7.83,7.7,8.58,9.57,9.38,9.12,9.7,9.56,10.5,10.6,10.5,11,12,12.8,13.1,14.1,14.5,15,16.2,17.5,18.3,18.8,20,21.1,22.3,23,24.3,25.5,27.4,28.9,29.5,30.9,32.2,33.8,35,37.4,38.5,40.6,41,43.9,45.2,47.3,48.7,50.6,52,54,56.2,57.7,60,61.8,63.6,65.6,68,70.4,73.1,75.1,77,80.6,83.5,86,90.1,93,96.6,104,108,111,116,119,123,126,130,133,138,141,146,149,153,156,157,157,157,157,157,157,158,156,158,156,157,156,157,157,158,157,159,158,157,156,157,157,157,156,157,157,157,157,158,157,154,146,138,130,122,115,109,102,95.3,81.5,79.2,72.4,67.9,63.7,60.2,55.8,52.2,48.7,44.7,41.7,41.7,35.4,31.8,28.2,25.5,23.4,20.8,18.7,16.8,15.4,14.1,12.9,11,10.3,9.82,9.5,8.7,8.19,7.6,7.22,8.27,7.91,8.42,8.61,8.83,9.16,9.57,9.66,9.95,9.99,10.5,10.9,12.1,11.9,12.5,13.9,14.3,14.2,15.2,16.1,17.3,17.3,18.4,19.6,20.6,21.5,22.1,23.7,24.6,26.1,27.2,28.7,30,31.9,32.8,34,35.6,36.7,38.3,40.3,41.6,43.1,44.8,46.5,49,49.9,52,53.4,55.9,58,59.8,62.3,64.8,67,69.4,72,73.2,76.4,79.3,82.4,85.5,88.6,91,94.2,97.9,106,110,113,117,120,124,126,132,134,139,140,143,148,150,150,152,154,153,152,153,152,152,152,152,152,153,152,152,154,153,154,154]; diff --git a/readme/measured_curve.json b/readme/measured_curve.json new file mode 100644 index 0000000..44354b2 --- /dev/null +++ b/readme/measured_curve.json @@ -0,0 +1,49 @@ +{ + "100": { + "is_valid": false, + "error": 0, + "ctrl_curve": { "50": 50 }, + "flow_curve": { "50": 0.019667187500000002 }, + "power_curve": { "50": 8.482656250000002 } + }, + "125": { + "is_valid": false, + "error": 0, + "ctrl_curve": { "100": 100 }, + "flow_curve": { "100": 0.0835 }, + "power_curve": { "100": 10.4 } + }, + "150": { + "is_valid": false, + "error": 0, + "ctrl_curve": { "200": 200 }, + "flow_curve": { "200": 0.32146874999999997 }, + "power_curve": { "200": 16.7125 } + }, + "175": { + "is_valid": true, + "error": 0, + "ctrl_curve": { "0": 0, "350": 350, "550": 550, "600": 600, "1000": 1000 }, + "flow_curve": { + "0": 0, + "350": 0.9294287109375, + "550": 0.941, + "600": 1.05, + "1000": 1.922000000000001 + }, + "power_curve": { + "0": 5.076000000000002, + "350": 39.11787109375, + "550": 64.8, + "600": 76.4, + "1000": 169.20000000000007 + } + }, + "200": { + "is_valid": false, + "error": 0, + "ctrl_curve": { "550": 550 }, + "flow_curve": { "550": 1.8175000000000001 }, + "power_curve": { "550": 94.55 } + } +} diff --git a/rotatingMachine.html b/rotatingMachine.html new file mode 100644 index 0000000..c5d4bfc --- /dev/null +++ b/rotatingMachine.html @@ -0,0 +1,297 @@ + + + + + + + \ No newline at end of file diff --git a/rotatingMachine.js b/rotatingMachine.js new file mode 100644 index 0000000..1205bc4 --- /dev/null +++ b/rotatingMachine.js @@ -0,0 +1,247 @@ +module.exports = function (RED) { + function rotatingMachine(config) { + RED.nodes.createNode(this, config); + var node = this; + + try { + // Load Machine class and curve data + const Machine = require("./dependencies/machine/machine"); + const OutputUtils = require("../generalFunctions/helper/outputUtils"); + + const machineConfig = { + general: { + name: config.name || "Default Machine", + id: node.id, + logging: { + enabled: config.eneableLog, + logLevel: config.logLevel + } + }, + asset: { + supplier: config.supplier || "Unknown", + type: config.machineType || "generic", + subType: config.subType || "generic", + model: config.model || "generic", + machineCurve: config.machineCurve + } + }; + + const stateConfig = { + general: { + logging: { + enabled: config.eneableLog, + logLevel: config.logLevel + } + }, + movement: { + speed: Number(config.speed) + }, + time: { + starting: Number(config.startup), + warmingup: Number(config.warmup), + stopping: Number(config.shutdown), + coolingdown: Number(config.cooldown) + } + }; + + // Create machine instance + const m = new Machine(machineConfig, stateConfig); + + // put m on node memory as source + node.source = m; + + //load output utils + const output = new OutputUtils(); + + function updateNodeStatus() { + try { + const mode = m.currentMode; + const state = m.state.getCurrentState(); + const flow = Math.round(m.measurements.type("flow").variant("predicted").position('downstream').getCurrentValue()); + const power = Math.round(m.measurements.type("power").variant("predicted").position('upstream').getCurrentValue()); + let symbolState; + switch(state){ + case "off": + symbolState = "⬛"; + break; + case "idle": + symbolState = "⏸️"; + break; + case "operational": + symbolState = "⏵️"; + break; + case "starting": + symbolState = "⏯️"; + break; + case "warmingup": + symbolState = "🔄"; + break; + case "accelerating": + symbolState = "⏩"; + break; + case "stopping": + symbolState = "⏹️"; + break; + case "coolingdown": + symbolState = "❄️"; + break; + case "decelerating": + symbolState = "⏪"; + break; + } + const position = m.state.getCurrentPosition(); + const roundedPosition = Math.round(position * 100) / 100; + + let status; + switch (state) { + case "off": + status = { fill: "red", shape: "dot", text: `${mode}: OFF` }; + break; + case "idle": + status = { fill: "blue", shape: "dot", text: `${mode}: ${symbolState}` }; + break; + case "operational": + status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}m³/h | ⚡${power}kW` }; + break; + case "starting": + status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; + break; + case "warmingup": + status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}m³/h | ⚡${power}kW` }; + break; + case "accelerating": + status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}%| 💨${flow}m³/h | ⚡${power}kW` }; + break; + case "stopping": + status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; + break; + case "coolingdown": + status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; + break; + case "decelerating": + status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} - ${roundedPosition}% | 💨${flow}m³/h | ⚡${power}kW` }; + break; + default: + status = { fill: "grey", shape: "dot", text: `${mode}: ${symbolState}` }; + } + return status; + } catch (error) { + node.error("Error in updateNodeStatus: " + error.message); + return { fill: "red", shape: "ring", text: "Status Error" }; + } + } + + function tick() { + try { + const status = updateNodeStatus(); + node.status(status); + + //get output + const classOutput = m.getOutput(); + const dbOutput = output.formatMsg(classOutput, m.config, "influxdb"); + const pOutput = output.formatMsg(classOutput, m.config, "process"); + + //console.log(pOutput); + + //only send output on values that changed + let msgs = []; + msgs[0] = pOutput; + msgs[1] = dbOutput; + + node.send(msgs); + + } catch (error) { + node.error("Error in tick function: " + error); + node.status({ fill: "red", shape: "ring", text: "Tick Error" }); + } + } + + // register child on first output this timeout is needed because of node - red stuff + setTimeout( + () => { + + /*---execute code on first start----*/ + let msgs = []; + + msgs[2] = { topic : "registerChild" , payload: node.id, positionVsParent: "upstream" }; + msgs[3] = { topic : "registerChild" , payload: node.id, positionVsParent: "downstream" }; + + //send msg + this.send(msgs); + }, + 100 + ); + + //declare refresh interval internal node + + setTimeout( + () => { + //---execute code on first start---- + this.interval_id = setInterval(function(){ tick() },1000) + }, + 1000 + ); + + node.on("input", function(msg, send, done) { + try { + + /* Update to complete event based node by putting the tick function after an input event */ + + + switch(msg.topic) { + case 'registerChild': + const childId = msg.payload; + const childObj = RED.nodes.getNode(childId); + m.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent); + break; + case 'setMode': + m.setMode(msg.payload); + break; + case 'execSequence': + const { source, action, parameter } = msg.payload; + m.handleInput(source, action, parameter); + break; + case 'execMovement': + const { source: mvSource, action: mvAction, setpoint } = msg.payload; + m.handleInput(mvSource, mvAction, Number(setpoint)); + break; + case 'flowMovement': + const { source: fmSource, action: fmAction, setpoint: fmSetpoint } = msg.payload; + m.handleInput(fmSource, fmAction, Number(fmSetpoint)); + + break; + case 'emergencystop': + const { source: esSource, action: esAction } = msg.payload; + m.handleInput(esSource, esAction); + break; + case 'showCompleteCurve': + m.showCompleteCurve(); + send({ topic : "Showing curve" , payload: m.showCompleteCurve() }); + break; + case 'CoG': + m.showCoG(); + send({ topic : "Showing CoG" , payload: m.showCoG() }); + break; + } + + if (done) done(); + } catch (error) { + node.error("Error processing input: " + error.message); + if (done) done(error); + } + }); + + node.on('close', function(done) { + if (node.interval_id) clearTimeout(node.interval_id); + if (node.tick_interval) clearInterval(node.tick_interval); + if (done) done(); + }); + + } catch (error) { + node.error("Fatal error in node initialization: " + error.stack); + node.status({fill: "red", shape: "ring", text: "Fatal Error"}); + } + } + + RED.nodes.registerType("rotatingMachine", rotatingMachine); +}; \ No newline at end of file