Equations and algorithm for Reverse Osmosis membranes comparison

Equations and algorithm for Reverse Osmosis membranes comparison

By Daniel
- Updated 2 months ago
Posted in:
0 comments

Comparison method for Reverse Osmosis membrane datasheets or pilot plants.

Reverse Osmosis and Nanofiltration membrane datasheets specifications are good for evaluating the quality of the delivered products but almost useless to compare products performance. The main reason for that is that every membrane was tested under different conditions (pressure, salinity, solution composition, recovery, pH and temperature).

Water mass transport coefficient (A-Value) and Salt mass transport coefficient (B-value)

RO and NF membranes can be defined by two parameters known in the industry as A and B-values.

 

  • A-Value represents the water permeability or the resulting flux from a specific driving pressure. high A-Value represent a lower operating pressure for the membrane. A-Values are measured in units of flux per unit pressure, for example, GFD/psi or LMH/bar, in the International System: m²/(m².s.Pa).
     
  • B-Value is the salt diffusion rate through the membrane. Every salt has it's own B-value according to it's chemical and physical properties as well as membrane charges and composition. A high B-Value represent a low salt rejection for the membrane. B-Values are measured in flux units: GFD or LMH, in the International System: m³/(m².s).

 

Comparing membranes

To be able to compare different membranes we use the results form the test conditions (as stated in the datasheets) to determinate the A and B-values of that element and compare them. Those values are specific for the salt used (NaCl, MgSO4, etc...) and not necessarily correlate with other compounds rejection like Silica or Boron but that's the only way to compare datasheets properly.

 

The algorithm

The algorithm below uses the equations provided by the membrane manufactures, it was calibrated for single element comparisons, for multiple elements or systems please check Equations and algorithm for Reverse Osmosis systems normalization.


The precision of the results get worst when leaving from the typical test conditions like recoveries up to 15% or salt concentrations up to 32000mg/L.


This algorithm was implemented in Plutocalc Water so if you just need to compare datasheets you can try this software that works on any computer and also in mobile phones.

 

Source code and equations

This source code was written in JavaScript but it can easily be ported to Java, C or any other language. If you need to see it in action please check the Plutocalc Water application.

/* =============================================================
/   * RO membrane comparison function for JavaScript
	* Developed by Daniel BP([email protected])
	*
	* Version 1.1
	*
	* http://www.danbp.org
	*
	* Copyright (C) 2016  Daniel BP
	*
/* =============================================================*/
function ro_comparison(user_inputs){
	
	//Declare variables

	//Inputs - Declare and import
	//Convert for different user input units in this section
	var SolName = user_inputs.SolName; //Solution name (string). See SProp below for allowed names.
	var Cf = user_inputs.Cf; //Feed Concentration (mg/L)
	var	Tf  = user_inputs.Tf; //Temperature (C)
	var	Pf  = user_inputs.Pf; //Feed Pressure (bar)
	var	Rec  = user_inputs.Rec; //Recovery (%)
	var	Qp_m3day  = user_inputs.Qp; //Product Flow (m³/day)
	var	Rej = user_inputs.Rej;//Salt Rejection (%)
	var	Area = user_inputs.Area; //Membrane Area (m²)
		
	//Calculation variables
	var Qp; //Product flow (m³/s)
	var Qr; //Reject flow (m³/s)
	var Qf; //Feed flow (m³/s)
	var Qfc; //Feed/concentrate average flow (m³/s)
	var TCF; //Temperature correction factor
	var Beta; //Polarization factor
	var CFR; //Average Feed/Concentrate Concentration Factor
	var Cp; //Permeate concentration (mg/L)
	var SMW; //Solute Molecular Weight (g/mol)
	var VHk; //Vant Hoff Coefficient
	var Ok; //Osmotic Coefficient
	var CMf; //Salt Molality in the Feed (mol/kg)
	var Rk = 0.08314462; //Universal Gas Constant (L.bar.(1/K).(1/mol))
	var Of; //Feed Osmotic Pressure (bar)
	var Op; //Permeate Osmotic Pressure (bar)
	var Ofc; //Average Feed/Concentrate Osmotic Pressure (bar)
	var Pd; //Pressure Drop (bar)
	var NDP; //Net Driving Pressure (bar)
	var A_SI; //Water mass transport coefficient at 25°C (m/(s.bar))
	var B_SI; //Salt mass transport coefficient at 25°C (m/s)
	var A_lmhbar; //Water mass transport coefficient at 25°C (LMH/bar)
	var B_lmh; //Salt mass transport coefficient at 25°C (LMH)
	var SProp; //Solution properties table
		
	//System variables
	var ErrorLog;
	var user_outputs;
	var incomplete_inputs = false;

	//Data tables
	//Solute name, Molecular Weight, Van't Hoff and Osmotic Coefficient
	SProp = [
	  ["NaCl",58.4428,2,0.93],
	  ["CaCl2",110.984,3,0.86],
	  ["Glucose",180.1559,1,1.01],
	  ["HCl",36.46094,2,0.95],
	  ["KCl",74.5513,2,0.92],
	  ["MgCl2",95.211,3,0.89],
	  ["MgSO4",120.3676,2,0.58],
	  ["Na2SO4",142.04,3,0.74],
	  ["NaHCO3",84.007,2,0.96],
	  ["NH4Cl",53.491,2,0.92],
	  ["Sucrose",342.2965,1,1.02]
	]

	 ErrorLog="";
	 
	//Check for invalid inputs (negative or zero)
	if(
		Cf<=0||
		Tf<=0||
		Pf<=0||
		Rec<=0||
		Qp_m3day<=0||
		Rej<=0||
		Area<=0
		) incomplete_inputs = true;
	 
	//Calculations
		
	if(Cf>100000) ErrorLog += "Concentration is higher than 10%, too much for the current osmotic pressure model.<BR>";
	if(Rec>20) ErrorLog += "Invalid recovery for a single element, max 20%.<BR>";
	if(Rej>100) ErrorLog += "Invalid rejection, max 100%.<BR>";
	if(Tf>80) ErrorLog += "Invalid temperature, max 80C.<BR>";
	if(Area>283) ErrorLog += "Area is too high for a single spiral element test.<BR>";
		
	Qp = Qp_m3day/24/3600; //Unit conversion
	Qf = Qp/(Rec/100); //Feed flow
	Qr = Qf-Qp; //Concentrate flow
	Qfc = (Qr+Qf)/2; //Average feed/concentrate flow
	if(Tf>25) TCF = Math.exp(2640*(1/298-1/(273+Tf))); //Temperature correction factor from FILMTEC Manual
	if(Tf<=25) TCF = Math.exp(3020*(1/298-1/(273+Tf))); //Temperature correction factor from FILMTEC Manual
	Beta = Math.exp(0.7*Rec/100); //DOW FILMTEC Equation, valid only for very low recovery rates
	CFR = 0.5*(1+((1-Rec/100*(1-Rej/100))/(1-Rec/100))); //DOW FILMTEC Equation
	Cp = Cf*(1-Rej/100); //Permeate concentration (mg/L)
	for(var i=0;i<SProp.length;i++)
		if(SProp[i][0] == SolName) {
			SMW = SProp[i][1]; //Solution Molecular Weight (from table)
			VHk = SProp[i][2]; //Van't Hoff coefficient (from table)
			Ok = SProp[i][3]; //Osmotic Coefficient (from table)	
		}
	CMf = (Cf/1000)/SMW; //Molality in the feed
	Of = VHk*Ok*CMf*Rk*(273+Tf); //Harmon Northrop Morse Equation for the Osmotic Pressure in the feed
	Op = Of*(1-Rej/100); //Osmotic Pressure in the permeate
	Ofc = Beta*CFR*Of; //Average feed/concentrate Osmotic Pressure
	Pd = 0.01*Math.pow(Qfc*15852,1.65)/14.5038; //DOW FILMTEC Equation with adjusted coefficient 1.65/14
	NDP = Pf-Pd/2-Ofc+Op; //Net Driving Pressure
	A_SI = Qp/(Area*TCF*NDP); //International system: m³/(m².s.bar)
	B_SI = Cp/(Cf*Beta*CFR*TCF*Area/Qp); //International system: m³/(m².s)
	A_lmhbar = A_SI*1000*3600; //Unit conversion: L/(m².h.bar) or LMH/bar
	B_lmh = B_SI*1000*3600; //Unit conversion: L/(m².h) or LMH
		
	//Output the results
	//Convert for different user output units in this section
	if(ErrorLog.length == 0 && incomplete_inputs == false)
		user_outputs = {
			ErrorLog: ErrorLog, //Error messages
			A: A_lmhbar, //Water mass transport coefficient at 25°C (LMH/bar)
			B: B_lmh, //Salt mass transport coefficient at 25°C (LMH)
			NDP: NDP, //Net Driving Pressure (bar)
			Pd: Pd, //Differential pressure (bar)
			Ofc: Ofc //Average feed/concentrate osmotic pressure (bar)
			};
	else
		user_outputs = {
			ErrorLog: ErrorLog,
			A: 0,
			B: 0,
			NDP: 0,
			Pd: 0,
			Ofc: 0
			};
		

	return user_outputs;

} //End of the procedure

/*********************************************************************/
//Usage example - results should display in the browser console
	var user_inputs =
	{
	SolName: "NaCl",
	Cf: 32000,
	Tf: 25,
	Pf: 58.95,
	Rec: 15,
	Qp: 28.39,
	Rej: 99.8,
	Area: 37.2
	};
	
	console.log(ro_comparison(user_inputs));

 

Tags