/*	Object Oriented Programming in javascript
	PIPE Object for Hydraulic calculations
	Created by BS
	8/3/2008
	UPDATED 9/25/2009
	MODIFIED 5/31/2010 changed circularpipe function names flowrate_y to flowrate and flowrate to flowrate_a to make it similar to rectangular pipe function names 
	ADDED Froude_y(y) function : Froude number based on y
*/
	function RectangularSection(Breadth,Tall)
	{
		//Defines Geometric Properties
		//
		this.getFlowArea=getFlowArea;
		this.getWetPerimeter=getWetPerimeter;
		this.getTopWidth=getTopWidth;
		this.getHydRadius=getHydRadius; 
		this.getEquivalentCircular=getEquivalentCircular;

		function getFlowArea(d)
		{
			//d=depth
			return Breadth*d;
		}

		function getWetPerimeter(d)
		{	
			//d=depth
			if(d==Tall)
			{return 2*Breadth+2*Tall;}	//ceiling top counted
			else
			{return Breadth+2*d;}	//open top no ceiling
		}
		function getTopWidth()
		{
			return Breadth;
		}

		function getHydRadius(d)
		{	//d=depth
			return getFlowArea(d)/getWetPerimeter(d);
		}		
	
		function getEquivalentCircular()
		{
			//equivalent circular diameter
			//both will have same capacity

			var c=2.0/Math.pow(Math.PI,(3.0/8.0))
			var numerator=Math.pow(Breadth*Tall,(5.0/8.0))
			var denom=Math.pow(Breadth+Tall,0.25)
			return c*numerator/denom;
		}
	}
	
	function CircularSection(diameter)
	{	
		//Defines Geometric Properties
		//Hydraulic Properties are not defined here
		//No units
		this.getAngle=getAngle; //half angle in radians
		this.getDepth=getDepth;
		this.getFlowArea=getFlowArea;
		this.getWetPerimeter=getWetPerimeter;
		this.getTopWidth=getTopWidth;

		this.getHydRadius=getHydRadius; //Hydraulic Radius

		function getAngle(y)
		{	//y=depth
			var D=diameter;
			return Math.acos(1-2*y/D);
		}		

		function getDepth(a)
		{	//a=half angle in radians
			return (1-Math.cos(a))*diameter/2;
		}
	
		function getFlowArea(a)
		{	//a=half angle in radians
			var D=diameter;
			return D*D*(2*a-Math.sin(2*a))/8;
		}

		function getWetPerimeter(a)
		{	//a=half angle in radians
			var D=diameter;
			return D*a;
		}

		function getTopWidth(a)
		{	//a=half angle in radians
			var D=diameter;
			var y=getDepth(a);
			return 2*Math.sqrt(D*y-y*y);
		}

		function getHydRadius(a)
		{	//a=half angle in radians
			var D=diameter;
			return getFlowArea(a)/getWetPerimeter(a);
		}		

	}

	function RectangularPipe(breadth,tall,n,slope)
	{	//Defines Hydraulic Properties //FPS Units

		this.velocity=velocity;
		this.surchargedVelocity=surchargedVelocity;
		this.surchargedflow=surchargedflow;
		this.surchargedHeadLoss=surchargedHeadLoss;
		this.flowrate=flowrate;
		this.normalDepth=normalDepth;
		this.frictionSlope=frictionSlope;
		this.Froude=Froude;
		this.Froude_y=Froude_y;

		function velocity(y)
		{
			var xsection=new RectangularSection(breadth,tall);
			return 1.486/n*Math.pow(xsection.getHydRadius(y),(2.0/3.0))*Math.sqrt(slope);
		}

		function surchargedVelocity(Q)
		{
			//ignore pipe slope
			//use friction slope

			var sf=frictionSlope(Q) 	//hydraulic gradient
			var hRad=breadth*tall/(2*(breadth+tall))
			return 1.486/n*Math.pow(hRad,(2.0/3.0))*Math.sqrt(sf);
		}

		function surchargedflow(totalHeadLoss,pipeLength,minorLossesFactor)
		{
			//based on total head loss dh when minor losses factor k is known

			var a=breadth*tall;
			var hRad=breadth*tall/(2*(breadth+tall));
			var q=n/1.486/a;				//temp calc
			q=q*q*pipeLength/Math.pow(hRad,4.0/3.0);	//temp calc
			q=q+minorLossesFactor/2/32.2/a/a;		//temp calc
			q=totalHeadLoss/q;				//temp calc
			var Q=Math.sqrt(q)

			return Q;	//flow cfs
		}

		function surchargedHeadLoss(flow,pLength,kminor)
		{
			//returns an array of results
			var Q=flow;	//cfs
			var lf=pLength;	//feet
			var k=kminor;	//minor losses coefficient summation

			var v=surchargedVelocity(Q);
			var sf=frictionSlope(Q);
			var hf=sf*lf;
			var hv=k*v*v/2/32.2;

			var a=[];
			a[0]=lf;	//feet length
			a[1]=breadth;	//feet width
			a[2]=tall	//feet tall
			a[3]=n;		//manning
			a[4]=Q;		//cfs
			a[5]=k;		//sum of k factors
			a[6]=v;		//vel
			a[7]=sf;	//hyd dradient
			a[8]=hf;	//frictional head loss
			a[9]=hv;	//vel head loss
			a[10]=hf+hv;	//total head loss

			return a;
		}

		function flowrate(y)
		{	//cfs
			var xsection=new RectangularSection(breadth,tall);
			return velocity(y)*xsection.getFlowArea(y);
		}

		function normalDepth(Q)
		{	//Normal Depth in Feet
			//Q in cu ft per sec
			//console.log('Running RectangularPipe.normalDepth(Q)..');
			var ymax=tall; var msg='normalDepth() counter : ';
			var ymin=0; var count=0;
			var MAX_ITERATIONS=1000;
			do
			{
				count++;
				if(count>MAX_ITERATIONS){return;}
				var y=(ymax+ymin)/2; msg=msg+'\n'+count+' : '+y;
				var err=(this.flowrate(y)-Q);
				if(err>0){ymax=y;}
				else{ymin=y;}
			}
			while(Math.abs(err)>0.001)
			//console.log(msg+count); 
			return y;
		}

		function frictionSlope(Q)
		{	
			var b=breadth;
			var t=tall;
			//head loss per unit length during surcharged flow condition
			//for a particular Q
			var a=b*t;	//area
			var p=2*(b+t)	//wet perimeter

			var x=n*Q/1.486/(a)/Math.pow((a/p),(2/3));
			return x*x;
		}

		function Froude(Q)
		{
			//Froude Number as a function of flow
			var yn=this.normalDepth(Q);
			var A=yn*breadth;
			var v=Q/A;
			return v*Math.sqrt(breadth/A/32.2);
		}

		function Froude_y(y)
		{
			//Froude Number as a function of normal depth
			if(y>=tall){return 0}
			else{
				var v=this.velocity(y);
				var A=y*breadth;
				return v*Math.sqrt(breadth/A/32.2);
			}
		}	
	}

	function CircularPipe(diameter,n,slope)
	{
		//Defines Hydraulic Properties
		//FPS Units

		this.velocity=velocity;
		this.surchargedVelocity=surchargedVelocity;
		this.surchargedflow=surchargedflow;
		this.surchargedHeadLoss=surchargedHeadLoss;
		this.flowrate_a=flowrate_a;
		this.flowrate=flowrate;
		this.normalDepth=normalDepth;
		this.criticalDepth=criticalDepth;
		this.getDepth=getDepth;
		this.getAngle=getAngle;
		this.getFlowArea=getFlowArea;
		this.getWetPerimeter=getWetPerimeter;
		this.getHydRadius=getHydRadius;
		this.getParameters=getParameters;
		this.getParameters=getParameters;
		this.frictionSlope=frictionSlope;
		this.Froude=Froude;
		this.Froude_y=Froude_y;
		
		function velocity(y)
		{	//Velocity Feet per Second
			var xsection= new CircularSection(diameter);
			var a = getAngle(y);
			var R=xsection.getHydRadius(a);
			return 1.486/n*Math.pow(R,(2.0/3.0))*Math.sqrt(slope);
		}

		function surchargedVelocity(Q)
		{
			//ignore pipe slope
			//use friction slope

			var sf=frictionSlope(Q)	//hydraulic gradient
			var hRad=diameter/4.0;
			return 1.486/n*Math.pow(hRad,(2.0/3.0))*Math.sqrt(sf);
		}


		function surchargedflow(totalHeadLoss,pipeLength,minorLossesFactor)
		{
			//based on total head loss dh when minor losses factor k is known

			var a=Math.PI/4*diameter*diameter;
			var hRad=diameter/4.0;
			var q=n/1.486/a;				//temp calc
			q=q*q*pipeLength/Math.pow(hRad,4.0/3.0);	//temp calc
			q=q+minorLossesFactor/2/32.2/a/a;		//temp calc
			q=totalHeadLoss/q;				//temp calc
			var Q=Math.sqrt(q)

			return Q;	//flow cfs
		}

		function surchargedHeadLoss(flow,pLength,kminor)
		{
			//returns an array of results
			var Q=flow;	//cfs
			var lf=pLength;	//feet
			var k=kminor;	//minor losses coefficient summation

			var v=surchargedVelocity(Q);
			var sf=frictionSlope(Q);
			var hf=sf*lf;
			var hv=k*v*v/2/32.2;

			var a=[];
			a[0]=lf;	//feet length
			a[1]=diameter;	//feet width
			a[2]=diameter;	//feet tall
			a[3]=n;		//manning
			a[4]=Q;		//cfs
			a[5]=k;		//sum of k factors
			a[6]=v;		//vel
			a[7]=sf;	//hyd gradient
			a[8]=hf;	//frictional head loss
			a[9]=hv;	//vel head loss
			a[10]=hf+hv;	//total head loss

			return a;
		}


		function flowrate_a(a)
		{	//Flowrate Cubic Feet per Second
			//Expressed as function of angle
			var xsection = new CircularSection(diameter);
			var R=xsection.getHydRadius(a);
			return velocity(getDepth(a))*xsection.getFlowArea(a);
		}

		function flowrate(y)
		{
			//Flowrate Cubic Feet per Second
			//As function of Normal Depth
			var c=new CircularSection(diameter);
			var a=c.getAngle(y);
			return flowrate_a(a);
		}

		function normalDepth(Q)
		{	//Normal Depth in Feet
			//Q in cu ft per sec
			//console.log('Running CircularPipe.normalDepth(Q) ..')
			var msg='normalDepth(Q)...\n';
			var amax=Math.PI; var msg='normalDepth() counter : ';
			var amin=0; var count=0; var errr=9999;
			var MAX_ITERATIONS=1000;
			do
			{
				count++;
				msg=msg+'\n'+count;

				if(count>MAX_ITERATIONS){return;}
				var a=(amax+amin)/2; 
				msg=msg+' a= '+a;

				var errr=(this.flowrate_a(a)-Q);
				if(errr>0){amax=a;} 
				else{amin=a;}
				msg=msg+' errr = '+errr;

			}
			while(Math.abs(errr)>0.001)
			//console.log(msg);
			var cSect=new CircularSection(diameter);
			return cSect.getDepth(a);
		}

		function criticalDepth(Q)
		{	//Return Critical Depth in Feet
			//Q in cfs
			var count=0; var xmin=0; var xmax=2*Math.PI;
			var MAX_ITERATIONS=1000;
			var xsection=new CircularSection(diameter);
			do
			{
				count++;
				if(count>MAX_ITERATIONS)
				{	alert('No Critical Depth'); return;}
				var x=(xmax+xmin)/2;
				var A=xsection.getFlowArea(x);
				var y=xsection.getDepth(x);
				var T=xsection.getTopWidth(x);

				var fx=A*A*A/T-Q*Q/32.2;
				if(fx<0) {xmin=x;}
				else {xmax=x;}
			}
			while(Math.abs(fx)>0.01)
			return y;
		}

		function getDepth(a)
		{	
			//Return depth for given half angle
			//No Unit
			//a in radian
			var xsection=new CircularSection(diameter);
			return xsection.getDepth(a);
		}
		
		function getAngle(y)
		{
			//Return half angle for given depth
			//Radian Unit
			//y=depth No Unit
			var xsection=new CircularSection(diameter);
			return xsection.getAngle(y);
		}

		function getFlowArea(a)
		{
			//Return flow area for given half angle
			var xsection=new CircularSection(diameter);
			return xsection.getFlowArea(a);
		}

		function getWetPerimeter(a)
		{
			//Return wetted perimeter for given half angle
			var xsection=new CircularSection(diameter);
			return xsection.getWetPerimeter(a);
		}

		function getHydRadius(a)
		{
			//Return hydraulic radius for given half angle
			var xsection=new CircularSection(diameter);
			return xsection.getHydRadius(a);
		}

		function getParameters()
		{	//Return parameters of circular pipe object
			var msg='';
			msg=msg+'D = ' + diameter+'; ';
			msg=msg+'n = ' + n +'; ';
			msg=msg+'s = ' + slope +'; ';
			return msg;			
		}

		function frictionSlope(Q)
		{	
			var d=diameter;
			//head loss per unit length during surcharged flow condition
			//for a particular Q
			var a=Math.PI/4*d*d;	//area
			var x=n*Q/1.486/(a)/Math.pow((d/4),(2/3));
			return x*x;
		}

		function Froude(Q)
		{
			var yn=this.normalDepth(Q);
			var xsection=new CircularSection(diameter);
			var a=xsection.getAngle(yn);
			var T=xsection.getTopWidth(a);
			var v = velocity(getDepth(a));
			var Fr=v  * v *T/this.getFlowArea(a)/32.2;
			return Math.sqrt(Fr);
		}

		function Froude_y(y)
		{
			//Froude number as a function of normal depth
			if(y>=diameter){return 0;}
				else{
				var xsection=new CircularSection(diameter);
				var a=xsection.getAngle(y);
				var T=xsection.getTopWidth(a);
				var v = velocity(y);
				var Fr=v  * v *T/this.getFlowArea(a)/32.2;
				return Math.sqrt(Fr);
			}
		}
	}
