function TurkeyLayer( name, percentRadius, turkeyModel, ovenModel ){ var that = this; this.name = name; this.initialTemp = 20; this.waterContent = 100000; this.Qdot = 0; this.finalTemperature = 20; return { updateTemperatureTick: function(){ that.finalTemperature = UtilityFunctions.transientSphereSeries( turkeyModel.density, turkeyModel.thermalConduct, turkeyModel.heatConvection, turkeyModel.cp, percentRadius * turkeyModel.totalRadius, turkeyModel.totalRadius, that.initialTemp, ovenModel.tempInfini, globalTime ); that.initialTemp = that.finalTemperature; } } } function TurkeyModel( weight, ovenModel ){ this.density = 996; // kg/m3 Assuming Density of Water 1000 kg/m3 this.cp = 2810; // J/kg K for Turkey this.heatConvection = 5; // W/m2 K Some Reasonable estimate for natural Convection. Change as needed. 5-25 this.thermalConduct = 0.412; // W/m K // Chicken this.totalRadius = UtilityFunctions.calculateRadius( weight, this.density ); this.totalLayers = [ new TurkeyLayer("Skin", 0.85, this, ovenModel ), new TurkeyLayer("Body", 0.45, this, ovenModel ), new TurkeyLayer("Core", 0.01, this, ovenModel ) ]; // Whenever temperature is changed this.updateLayerTemps = function() { for (var i in this.totalLayers ){ this.totalLayers[i].updateTemperatureTick(); } } } function OvenModel() { this.tempInfini=20; //C this.setTemp = 20; var proportional = 0.1; // This value is arbitrary to how fast you want the temperatures to converge. (Or oscillate, which could be realistic as well) var errorTolerance = 5; //Stove is accurate to 1 degree Celcius Should hopefully oscillate below that value. this.changeTemp = function(setTemp) { this.setTemp = setTemp; } // Equalize temp will need to be sent each time iteration this.equalizeTemp = function() { var error = Math.abs(this.setTemp-this.tempInfini); if( this.setTemp>this.tempInfini ){ this.tempInfini = this.tempInfini + error*proportional; } else if( this.setTemperrorTolerance ) { return (true) //Need to run the Heat Calculations again next cycle } } } UtilityFunctions = { // Cache the lambda if the Biot number does not change, to avoid expensive root-finding operations cachedBiot: null, cachedLambda: null, // Using Ratios for a rectangular Box Turkey calculateRadius: function(weight, density) { var ratioLvG=1.4; //1.4, Turkey length vs shoulder girth var ratioLvH=2; //2, Turkey length vs height from resting position var length = Math.pow(weight/((1/ratioLvG)*(1/ratioLvH)*density),(1/3)) var depth = 1/(ratioLvG /length); var height = 1/(ratioLvH /length); var simpleRadius = length/2; //Doesn't take into account equal Volume var rectangleVolume = depth*height*length*(1/3); //m^3 Multiple by 1/3 to account for triangular shape and empty Space var complexRadius = Math.pow(rectangleVolume/((4/3)*Math.PI), 1/3); //Volume of 3D Box = 3D Sphere console.log("Simple Radius " + simpleRadius + " Meters") console.log("Complex Radius " + complexRadius + " Meters") return complexRadius; }, findAllRoots: function(min,max,splitsNum,Biot) { var step = ( max - min ) / ( splitsNum - 1 ); var answer; var negativeTest; var storage = []; for (var i = step; i < max; i=i+step ) { negativeTest = this.lambdaFormula(i-step, Biot)*this.lambdaFormula(i, Biot); if (negativeTest <= 0) { answer = this.bisectionMethod(i-step,i,Biot); if (answer !=undefined) { storage.push(answer); } } else { //console.log("No Bracketed Root " + negativeTest) } } return storage; }, bisectionMethod: function(min,max,Biot) { errorTolerance = (1/Math.pow(10,8)) result = Infinity // some large value to ensure the calculation goes through. negativeTest =this.lambdaFormula(min, Biot)*this.lambdaFormula(max, Biot) if (negativeTest <=0 ) { var antiFreeze=0; while (Math.abs(result) > errorTolerance && antiFreeze<=500) { //The greater the antiFreeze, the more wasted cycles around a singularity lambdaN = (min+max)/2 result=this.lambdaFormula(lambdaN, Biot) if (Math.abs(result) <= errorTolerance && result<=errorTolerance) { return (lambdaN); //At Root } else if ((this.lambdaFormula(min, Biot)*this.lambdaFormula(lambdaN, Biot))>=0) { min=lambdaN; } else if ((this.lambdaFormula(max, Biot)*this.lambdaFormula(lambdaN, Biot))>=0) { max=lambdaN; } antiFreeze++ } } }, lambdaFormula: function( lambdaN, Biot ) { var result = 1-lambdaN*(1/Math.tan(lambdaN))-Biot; return(result) }, transientSphereSeries: function( density, thermalConduct, heatConvection, cp, rPosition, rTotal, tempInitial, tempInfini, t ){ var min = 0; var max = 1000; // This are for setting Lambda boundaries and nothing else var sum=0; var alpha = thermalConduct/(density*cp); var lambdaN; var sinPortion; var exponentialPortion; var frontCoefficientPortion; //console.log("Alpha is " + alpha) var Fourier = (alpha*t)/Math.pow(rTotal,2) //console.log("Fourier is " + Fourier) var biotNum = heatConvection * rTotal/thermalConduct if ( biotNum != this.cachedBiot ) { console.log("Recalculating Lambda Terms") this.cachedLambda = this.findAllRoots(min,max,max*Math.PI*10,biotNum) this.cachedBiot = biotNum; } //console.log("The Biot Value is " + biotNum) for (var i = 0; i