Equations and algorithm for Reverse Osmosis membranes comparison
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));