Home » Eclipse Projects » 4DIAC - Framework for Distributed Industrial Automation and Control » How to create IEC 61131-3 callable function in 4diac?
How to create IEC 61131-3 callable function in 4diac? [message #1863567] |
Sat, 10 February 2024 17:28 |
Ketut Kumajaya Messages: 26 Registered: January 2024 |
Junior Member |
|
|
IEC 61131-3 SECANTF function:
FUNCTION SECANTF : REAL
VAR_INPUT
X : REAL;
Y : REAL;
P : UINT;
END_VAR
(* Ketut Kumajaya, 20/08/2018 *)
(* Reference: https://lar.bnl.gov/properties/basic.html *)
(* Reference: http://edge.rit.edu/edge/P07106/public/Nox.pdf *)
IF (P = 3) THEN SECANTF := -Y + 4.863 * EXP((-5.9409785*(1.0 - X) + 1.3553888*EXPT(1.0 - X,1.5) - 0.46497607*EXPT(1.0 - X,2) - 1.5399043*EXPT(1.0 - X,4.5))/X);
ELSE IF (P = 5) THEN SECANTF := -Y + 7251.0 * EXP((-6.71893 * (1.0 - X) + 1.35966 * EXPT((1.0 - X),1.5) - 1.3779 * EXPT((1.0 - X),2.5) - 4.051 * EXPT((1.0 - X),5))/X);
ELSE SECANTF := 0.0;
END_IF;
END_IF;
END_FUNCTION
IEC 61131-3 SECANT function than call SECANTF function:
FUNCTION SECANT : REAL
VAR_INPUT
X1 : REAL;
X2 : REAL;
E1 : REAL;
Y1 : REAL;
P1 : UINT;
END_VAR
VAR
XM1 : REAL;
X0 : REAL;
C1 : REAL;
END_VAR
(* Ketut Kumajaya, 20/08/2018 *)
(* Adapted from: https://www.geeksforgeeks.org/program-to-find-root-of-an-equations-using-secant-method/ *)
XM1 := E1 + 0.1; (* make sure to initially enter the loop *)
IF (SECANTF(X1,Y1,P1) * SECANTF(X2,Y1,P1) < 0.0) THEN
(* repeat the loop until the convergence *)
WHILE (ABS(XM1 - X0) >= E1) DO
(* calculate the intermediate value *)
X0 := (X1 * SECANTF(X2,Y1,P1) - X2 * SECANTF(X1,Y1,P1)) / (SECANTF(X2,Y1,P1) - SECANTF(X1,Y1,P1));
(* check if x0 is root of equation or not *)
C1 := SECANTF(X1,Y1,P1) * SECANTF(X0,Y1,P1);
(* update the value of interval *)
X1 := X2;
X2 := X0;
(* if x0 is the root of equation then break the loop *)
IF (C1 = 0.0) THEN
EXIT;
END_IF;
XM1 := (X1 * SECANTF(X2,Y1,P1) - X2 * SECANTF(X1,Y1,P1)) / (SECANTF(X2,Y1,P1) - SECANTF(X1,Y1,P1));
END_WHILE;
SECANT := X0;
ELSE
SECANT := 0.0;
END_IF;
END_FUNCTION
IEC 61499 SECANTF FB:
ALGORITHM calculate
(* Ketut Kumajaya, 20/08/2018 *)
(* Reference: https://lar.bnl.gov/properties/basic.html *)
(* Reference: http://edge.rit.edu/edge/P07106/public/Nox.pdf *)
IF (QI = 3) THEN (* argon function *)
OUT := -Y + 4.863 * EXP((-5.9409785 * (1.0 - X) + 1.3553888 * EXPT(1.0 - X, 1.5) - 0.46497607 * EXPT(1.0 - X, 2) - 1.5399043 * EXPT(1.0 - X, 4.5)) / X);
ELSE
IF (QI = 5) THEN (* nitrous oxide function *)
OUT := -Y + 7251.0 * EXP((-6.71893 * (1.0 - X) + 1.35966 * EXPT((1.0 - X), 1.5) - 1.3779 * EXPT((1.0 - X), 2.5) - 4.051 * EXPT((1.0 - X), 5)) / X);
ELSE
OUT := 0.0;
END_IF;
END_IF;
END_ALGORITHM
IEC 61499 SECANT FB that call SECANTF FB. SECANTF1 is a SECANTF function block type registered inside SECANT FB:
ALGORITHM calculate
(* Ketut Kumajaya, 20/08/2018 *)
(* Adapted from: https://www.geeksforgeeks.org/program-to-find-root-of-an-equations-using-secant-method/ *)
XM1 := E1 + 0.1; (* make sure to initially enter the loop *)
(* IF (SECANTF(QI, X1, Y1) * SECANTF(QI, X2, Y1) < 0.0) THEN *)
SECANTF1(QI := QI, X := X1, Y := Y1);
Z1 := SECANTF1.OUT;
SECANTF1(QI := QI, X := X2, Y := Y1);
Z1 := Z1 * SECANTF1.OUT;
IF (Z1 < 0.0) THEN
(* repeat the loop until the convergence *)
WHILE (ABS(XM1 - X0) >= E1) DO
(* calculate the intermediate value *)
(* X0 := (X1 * SECANTF(QI, X2, Y1) - X2 * SECANTF(QI, X1, Y1)) / (SECANTF(QI, X2, Y1) - SECANTF(QI, X1, Y1)); *)
SECANTF1(QI := QI, X := X2, Y := Y1);
Z1 := X1 * SECANTF1.OUT;
SECANTF1(QI := QI, X := X1, Y := Y1);
Z1 := Z1 - X2 * SECANTF1.OUT;
SECANTF1(QI := QI, X := X2, Y := Y1);
X0 := SECANTF1.OUT;
SECANTF1(QI := QI, X := X1, Y := Y1);
X0 := X0 - SECANTF1.OUT;
X0 := Z1 / X0;
(* check if x0 is root of equation or not *)
(* C1 := SECANTF(QI, X1, Y1) * SECANTF(QI, X0, Y1) *)
SECANTF1(QI := QI, X := X1, Y := Y1);
C1 := SECANTF1.OUT;
SECANTF1(QI := QI, X := X0, Y := Y1);
C1 := C1 * SECANTF1.OUT;
(* update the value of interval *)
X1 := X2;
X2 := X0;
(* if x0 is the root of equation then break the loop *)
IF (C1 = 0.0) THEN
EXIT;
END_IF;
(* XM1 := (X1 * SECANTF(QI, X2, Y1) - X2 * SECANTF(QI, X1, Y1)) / (SECANTF(QI, X2, Y1) - SECANTF(QI, X1, Y1)); *)
SECANTF1(QI := QI, X := X2, Y := Y1);
Z1 := X1 * SECANTF1.OUT;
SECANTF1(QI := QI, X := X1, Y := Y1);
Z1 := Z1 - X2 * SECANTF1.OUT;
SECANTF1(QI := QI, X := X2, Y := Y1);
XM1 := SECANTF1.OUT;
SECANTF1(QI := QI, X := X1, Y := Y1);
XM1 := XM1 - SECANTF1.OUT;
XM1 := Z1 / XM1;
END_WHILE;
OUT := X0;
ELSE
OUT := 0.0;
END_IF;
END_ALGORITHM
Is my implementation above correct or is there any a simpler way? It looks complicated but works.
EDIT:
I can make a C function call inside exported FB easily but I just want to write my FB completely in ST language.
[Updated on: Sun, 11 February 2024 01:29] Report message to a moderator
|
|
|
Re: How to create IEC 61131-3 callable function in 4diac? [message #1863571 is a reply to message #1863567] |
Sun, 11 February 2024 11:27 |
|
I think having the code in ST is definitely the better solution then in C. Especially from a maintainability and portability perspective.
As there is lot of math involved I have a hard time to say if it is correct. But I would have some general observations that may help you making your code more readable:
- Functions and Function blocks may not have the same name
- if your function is only used by one function block you can define the function as method inside of the function block.
- It loooks like your SECANTF Function is rather expensive. From what I see you always invoke it with two different parameter sets. Doing this once before all your ifs and storing the results in two variables leads to better performance but also to better readable code.
- why is SECANTF an FB and not a method of SECANT FB? A method can access inputs and outputs of the FB. In your case this means you could avoid the QI parameter which again results in a bit better readability of your code.
I hope this helps.
|
|
|
Re: How to create IEC 61131-3 callable function in 4diac? [message #1863572 is a reply to message #1863571] |
Sun, 11 February 2024 17:59 |
Ketut Kumajaya Messages: 26 Registered: January 2024 |
Junior Member |
|
|
METHOD SECANT_FUNC : REAL
VAR_INPUT
SECANT_FUNC_X : REAL;
END_VAR
(* Ketut Kumajaya, 20/08/2018 *)
(* Reference: https://lar.bnl.gov/properties/basic.html *)
(* Reference: http://edge.rit.edu/edge/P07106/public/Nox.pdf *)
IF (SECANT_F = 3) THEN (* argon function *)
SECANT_FUNC := -SECANT_Y + 4.863 * EXP((-5.9409785 * (1.0 - SECANT_FUNC_X) + 1.3553888 * EXPT(1.0 - SECANT_FUNC_X,
1.5) - 0.46497607 * EXPT(1.0 - SECANT_FUNC_X, 2.0) - 1.5399043 * EXPT(1.0 - SECANT_FUNC_X, 4.5)) / SECANT_FUNC_X);
ELSE
IF (SECANT_F = 5) THEN (* nitrous oxide function *)
SECANT_FUNC := -SECANT_Y + 7251.0 * EXP((-6.71893 * (1.0 - SECANT_FUNC_X) + 1.35966 * EXPT((1.0 - SECANT_FUNC_X)
, 1.5) - 1.3779 * EXPT((1.0 - SECANT_FUNC_X), 2.5) - 4.051 * EXPT((1.0 - SECANT_FUNC_X), 5)) / SECANT_FUNC_X);
ELSE
SECANT_FUNC := 0.0; (* for future function *)
END_IF;
END_IF;
END_METHOD
METHOD SECANT : REAL
VAR_INPUT
SECANT_X1 : REAL;
SECANT_X2 : REAL;
SECANT_E : REAL;
END_VAR
VAR_TEMP
SECANT_XM : REAL;
SECANT_X0 : REAL;
SECANT_C : REAL;
SECANT_X11 : REAL;
SECANT_X21 : REAL;
END_VAR
(* Ketut Kumajaya, 20/08/2018 *)
(* Adapted from: https://www.geeksforgeeks.org/program-to-find-root-of-an-equations-using-secant-method/ *)
SECANT_X11 := SECANT_X1; (* to avoid writing to input variable *)
SECANT_X21 := SECANT_X2; (* to avoid writing to input variable *)
SECANT_XM := SECANT_E + 0.1; (* make sure to initially enter the loop *)
IF (SECANT_FUNC(SECANT_X11) * SECANT_FUNC(SECANT_X21) < 0.0) THEN
(* repeat the loop until the convergence *)
WHILE (ABS(SECANT_XM - SECANT_X0) >= SECANT_E) DO
(* calculate the intermediate value *)
SECANT_X0 := (SECANT_X11 * SECANT_FUNC(SECANT_X21) - SECANT_X21 * SECANT_FUNC(SECANT_X11)) / (SECANT_FUNC(
SECANT_X21)
- SECANT_FUNC(SECANT_X11));
(* check if SECANT_X0 is root of equation or not *)
SECANT_C := SECANT_FUNC(SECANT_X11) * SECANT_FUNC(SECANT_X0);
(* update the value of interval *)
SECANT_X11 := SECANT_X21;
SECANT_X21 := SECANT_X0;
(* if SECANT_X0 is the root of equation then break the loop *)
IF (SECANT_C = 0.0) THEN
EXIT;
END_IF;
SECANT_XM := (SECANT_X11 * SECANT_FUNC(SECANT_X21) - SECANT_X21 * SECANT_FUNC(SECANT_X11)) / (SECANT_FUNC(
SECANT_X21)
- SECANT_FUNC(SECANT_X11));
END_WHILE;
SECANT := SECANT_X0;
ELSE
SECANT := 0.0;
END_IF;
END_METHOD
ALGORITHM calculate
...
Y1 := Y1 + 1.01325; (* absolute pressure *)
SECANT_Y := Y1 / 10.0;
SECANT_F := 3;
Y1 := -273.15 + SECANT(0.55615813, 1.0, 0.000001) * 150.687;
...
END_ALGORITHM
My function block implementation much better now. I set SECANT_F and SECANT_Y variable first and then call SECANT method per as your recommendation. Thank you very much.
[Updated on: Mon, 12 February 2024 01:31] Report message to a moderator
|
|
| |
Goto Forum:
Current Time: Thu Jun 13 04:14:38 GMT 2024
Powered by FUDForum. Page generated in 0.03681 seconds
|