# Equations and algorithm for Reverse Osmosis systems normalization

On this page you will find a normalization algorithm that can be built in the PLC or in a SCADA system, therefore eliminating the need of the spreadsheets. This algorithm will deliver the normalized results in three variables: Pressure Drop, Permeability and Salt Passage rate. It also can consider the Concentration Polarization factor for a higher precision.

Normalization is a conversion of operational data that allows the user to compare operation at a specific set of conditions to a reference set of conditions in Reverse Osmosis plants. This allows the user to determine whether changes in flow or rejection are caused by fouling, damage to the membrane, or are just due to different operating conditions: temperature, flow and recovery changes and feed water salinity variations.

The RO normalization equations are described in the ASTM D 4516 (2010) and most membrane manufactures distribute free spreadsheets that have slightly improved calculations over the described in the ASTM.

## 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. The

**A-Value**represents the water permeability or the resulting flux from a specific driving pressure. The A-value is directly proportional to the Normalized Permeate Flow, most manufacturers recommend cleaning the membranes when those parameters drop 10% from the stabilized startup reference. A lower A-Value after a few months of operation most likely is an indication of fouling. A-Value is measured in 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 for a specific membrane according to it's chemical and physical properties and the Normalization model considers all salts as if they were NaCl. The B-value is directly proportional to the Normalized Salt Rejection, most manufacturers recommend cleaning the modules when those parameters increase 10%. A higher B-Value after a few months of operation might indicate fouling or membrane degradation (abrasion, oxidation, etc...). B-Value is measured in flux units: GFD or LMH, in the International System: m³/(m².s).

## Why use permeability and salt diffusion rate and not Normalized Flow and Normalized Rejection?

By using the permeability (A-value) and salt diffusion rate (B-value) we can directly compare different plants and different membranes in the same scale. That's impossible using the ASTM terms since a plant can have a normalized flow of 100m³/h and another of 25m³/h.

The A and B-values are also consistent with membrane datasheets data as described in Equations and algorithm for Reverse Osmosis membranes comparison and scientific papers.

## The algorithm

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

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

The algorithm can also take into consideration (optional) the concentration polarization factor for higher precision (thanks to LG). Please notice that the Concentration Polarization is not part of the ASTM D 4516 equation set.

## 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 normalization and plant comparison function for JavaScript
* Developed by Daniel BP ([email protected])
*
* Version 1.0
*
* http://www.danbp.org
*
* Copyright (C) 2016 Daniel BP
*
/* =============================================================*/
function ro_normalize(user_inputs){
//Declare variables
//Inputs - Declare and import
//Convert for different user input units in this section
var Tf = user_inputs.Tf; //Temperature (C)
var Pf = user_inputs.Pf; //Feed Pressure (bar)
var Pc = user_inputs.Pc; //Concentrate Pressure (bar)
var Pp = user_inputs.Pp; //Permeate Pressure (bar)
var Qp_m3h = user_inputs.Qp; //Product Flow (m³/h)
var Qc_m3h = user_inputs.Qc; //Concentrate Flow (m³/h)
var Uf = user_inputs.Uf; //Feed conductivity (micro-siemens/cm)
var Up = user_inputs.Up; //Feed conductivity (micro-siemens/cm)
var Area = user_inputs.Area; //Membrane Area (m²)
var UsePolFact = user_inputs.UsePolFact; //Use (true) or don't use the polarization factor (false)
//Calculation variables
var Cf; //Feed concentration (mg/L as NaCl)
var Cp; //Permeate concentration (mg/L as NaCl)
var Rej; //Salt Rejection (%)
var Qp; //Product flow (m³/s)
var Qf; //Feed flow (m³/s)
var Rec; //Recovery (%)
var Beta; //Concentration polarization factor
var TCF; //Temperature correction factor
var CFR; //Average Feed/Concentrate Concentration Factor
var Cfc; //Average Feed/Concentrate Concentration (mg/L)
var Ofc; //Average Feed/Concentrate Osmotic Pressure (bar)
var Op; //Permeate Osmotic Pressure (bar)
var Pd; //Pressure Drop (bar)
var NDP; //Net Driving Pressure (bar)
var A_SI; //Water mass transport coefficient (m/(s.bar))
var B_SI; //Salt mass transport coefficient (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
//none
ErrorLog="";
//Check for invalid inputs (negative or zero)
if(
Tf<=0||
Pf<=0||
Pc<=0||
Pp<0||
Qp_m3h<=0||
Qc_m3h<=0||
Uf<=0||
Up<=0||
Area<=0
) incomplete_inputs = true;
//Calculations
if(Tf>80) ErrorLog += "Temperature is too high for the current model. Max 80C.<BR>";
if(Uf>7630) Cf = (0.0000000000801*Math.exp(Math.pow((-50.6458-Math.log(Uf)),2)/112.484)); //Conductivity to mg/L approximation by DOW FILMTEC FTNorm
if(Uf<=7630) Cf = (7.7E-20*Math.exp(Math.pow((-90.4756-Math.log(Uf)),2)/188.884)); //Conductivity to mg/L approximation by DOW FILMTEC FTNorm
if(Up>7630) Cp = (0.0000000000801*Math.exp(Math.pow((-50.6458-Math.log(Up)),2)/112.484)); //Conductivity to mg/L approximation by DOW FILMTEC FTNorm
if(Up<=7630) Cp = (7.7E-20*Math.exp(Math.pow((-90.4756-Math.log(Up)),2)/188.884)); //Conductivity to mg/L approximation by DOW FILMTEC FTNorm
if(Cf>100000) ErrorLog += "Concentration in the feed is higher than 10%, too much for the current osmotic pressure model.<BR>";
if(Cp>100000) ErrorLog += "Concentration in the permeate is higher than 10%, too much for the current osmotic pressure model.<BR>";
Rej = (1-Cp/Cf)*100; //Rejection in %
Qp = Qp_m3h/3600; //Flow conversion
Qf = (Qp_m3h+Qc_m3h)/3600; //Flow conversion
Rec = Qp/Qf*100; //Recovery in %
if(UsePolFact == true) {Beta = Math.pow(Math.exp(0.75*2*Rec/100/(2-Rec/100)),(1/8));} //Calculation from LG NanoH2O normalization spreadsheet
else Beta = 1; //If not using the concentration polarization factor
if(Tf>25)TCF = Math.exp(2640*(1/298-1/(273+Tf))); //Calculation according to DOW FILMTEC FTNorm
if(Tf<=25)TCF = Math.exp(3020*(1/298-1/(273+Tf))); //Calculation according to DOW FILMTEC FTNorm
CFR = (Math.log(1/(1-(Rec/100)))/(Rec/100)); //Calculation according to the ASTM D4516
Cfc = Cf*CFR*Beta; //Average feed/concentrate concentration
Ofc = 0.0385*Cfc*(Tf + 273.15)/(1000-(Cfc/1000))/14.5038; //Calculation according to DOW FILMTEC FTNorm
Op = 0.0385*Cp*(Tf + 273.15)/(1000-(Cp/1000))/14.5038; //Calculation according to DOW FILMTEC FTNorm
Pd = Pf-Pc; //Differential pressure
NDP = Pf-Pd/2-Ofc+Op-Pp; //Net Driving Pressure
A_SI = Qp/(Area*TCF*NDP); //International system: m³/(m².s.bar)
B_SI = Cp/(Cfc*TCF*Area/Qp); //International system: m³/(m².s)
A_lmhbar = A_SI*1000*3600; //Permeability at 25°C unit conversion: L/(m².h.bar) or LMH/bar
B_lmh = B_SI*1000*3600; //Permeability at 25°C 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, //Differetial Pressure (bar)
Rec: Rec, //Recovery (%)
Rej: Rej, //Rejection (%)
Ofc: Ofc, //Average feed/concentrate osmotic pressure
Cp: Cp, //Permeate TDS
Cf: Cf, //Feed TDS
Beta: Beta //Concentration polarization factor
};
else
user_outputs = {
ErrorLog: ErrorLog,
A: 0,
B: 0,
NDP: 0,
Pd: 0,
Rec: 0,
Rej: 0,
Ofc: 0,
Cp: 0,
Cf: 0,
Beta: 0,
};
return user_outputs;
} //End of the procedure
/****************************************************************************/
//Usage example - results should display in the browser console
var user_inputs =
{
Tf: 32,
Pf: 32,
Pc: 28,
Pp: 1,
Qp: 50,
Qc: 10,
Uf: 20000,
Up: 150,
Area: 1860,
UsePolFact: true
};
console.log(ro_normalize(user_inputs));
```