NAG DLLs 与 Delphi
当您要从 Delphi 中调用 NAG 函数时,需要特别注意的一点是实际的参数必须是 var 型态。这是因为 Fortran 调用协议是以 reference 方式传递而非不是 value。您并不需要在编译过程将 NAG 算法库加入 Delphi,DLL 能够直接被程序本身调用且编译器会自动链接。
参照到 DLL 的方式是透过 procedure 或 function,并在 Delphi 的程序中以 external 方式来定义。函数必须与 DLL 中将被调用的 NAG 函数名称相同。Delphi 是区分大小写的,因此 NAG 的函数名称必须以大写表示。
参考以下的例子:
function S18AEF(var X : Double;
var IFAIL : Integer): Double;
stdcall;
external 'nagsx.dll';
其中的 stdcall 指令是用来指定使用到的函数,Fortran 的 DLL 函数必须以此方式调用。所以 NAG 函数可以透过以下的方式进行调用:
WriteLn(S18AEF(X, IFAIL));
多维度数组
若要将多维的数组传递到 Fortran DLL 中,必须要将数组进行转换。这是因为 Fortran 语言的数组型态储存形式与 Delphi 不同,例如 A[2,2] 以列为主的连续位置方式储存,其连续位置是 A[1,1], A[2,1], A[1,2], A[2,2]。 但是,对 Pascal 来说,储存方式是以行为主的型态,则是以 A[1,1], A[1,2], A[2,1], A[2,2] 为连续值。 请注意在例子中,Pascal 的数组做为一个实际传递到 Fortran DLL 函数的参数时,必须定义为资料型别。 Pascal 的数组变量是在 var 中所定义的,若以实际的参数覆盖其他的参数值,将会导致系统出错。
传递 Functions 与 Procedures
许多的 NAG 函数在被调用时是需要用户传递函数作为其输入的参数。 为了要能在 Delphi 中传递,每个要被调用的 procedure 或 function 必须要有自己的资料型态,并在 type 区块中被定义。 这样便能够将一系列的参数传递到 DLL 中。型别的定义必须要有函数本身有相同的数量与型态。 请注意,并不需要 var 的定义,因为在传递过程中只有一个副本。另外,stdcall 必须在 function/procedure 的定义中或在资料型别的定义中使用的,用来确保符合正确的调用方式。
以 Delphi 调用 D03PCF 函数示例
以下程序调用 NAG D03PCF 函数,用来计算线性或非线性拋物线偏微分方程 (PDEs)。可以在 NAG 的 DLL NAGD03.DLL 中找到。 此程序说明如何使用多维度数组并如所述传递 function/procedure。请注意此示例为了取得 Phi 值同时也调用到 X01AAF 函数,可在 NAGSX.DLL 中找到。
unit D03Code;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, TForms, Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
TForm1: TForm1;
implementation
{$R *.DFM} {Compiler Directive}
type
U_ArrayType = array [1..20, 1..2] of Double;
UOUT_ArrayType = array [1..1, 1..6, 1..2] of Double;
{请注意:以上的两个数组必须要定义成转置的型态,以确保调用 Fortran DLL 时能够兼容。}
W_ArrayType = array [1..1128] of Double; {1..NW}
X_ArrayType = array [1..20] of Double; {1..NPTS}
XOUT_ArrayType = array [1..6] of Double; {1..INTPTS}
IW_ArrayType = array [1..64] of Integer; {1..NIW}
NPDE_ArrayType = array [1..2] of Double; {1..NPDE}
P_ArrayType = array [1..2] of NPDE_ArrayType;
PDEDEFType = Procedure(var NPDE : Integer;
var T : Double;
var X : Double;
var U : NPDE_ArrayType;
var DUDX : NPDE_ArrayType;
var P : P_ArrayType;
var Q : NPDE_ArrayType;
var R : NPDE_ArrayType;
var IRES : Integer);
stdcall;
BNDARYType = Procedure(var NPDE : Integer;
var T : Double;
var U : NPDE_ArrayType;
var UX : NPDE_ArrayType;
var IBND : Integer;
var BETA : NPDE_ArrayType;
var GAMMA : NPDE_ArrayType;
var IRES : Integer);
stdcall;
{以上的两个 types 是 Procedure 型别。必须要将 BNDARY 与 PDEDEF 定义成此型态,才能以参数方式传递 procedure 到 DLL 中。}
var
NPDE : Integer = 2;
NPTS : Integer = 20;
INTPTS : Integer = 6;
ITYPE : Integer = 1;
NEQN : Integer;
NIW : Integer;
NWK : Integer;
NW : Integer;
I : Integer;
J : Integer;
IFAIL : Integer;
ALPHA : Double;
ACC : Double;
HX : Double;
PI : Double;
PIBY2 : Double;
TOUT : Double;
TS : Double;
IND : Integer;
IT : Integer;
ITASK : Integer;
ITRACE : Integer;
M : Integer;
U : U_ArrayType;
UOUT : UOUT_ArrayType;
W : W_ArrayType;
X : X_ArrayType;
XOUT : XOUT_ArrayType = (0.0,0.4,0.6,0.8,0.9,1.0);
IW : IW_ArrayType;
Procedure D03PCF(var NPDE : Integer;
var M : Integer;
var TS : Double;
var TOUT : Double;
PDEDEF : PDEDEFType; {The two procedure parameters,}
BNDARY : BNDARYType; {defined above under type}
var U : U_ArrayType;
var NPTS : Integer;
var X : X_ArrayType;
var ACC : Double;
var W : W_ArrayType;
var NW : Integer;
var IW : IW_ArrayType;
var NIW : Integer;
var ITASK : Integer;
var ITRACE : Integer;
var IND : Integer;
var IFAIL : Integer);
stdcall;
external 'nagD03.dll';
Function X01AAF(var PI : Double) : Double; stdcall;
external 'nagsx.dll';
Procedure D03PZF(var NPDE : Integer;
var M : Integer;
var U : U_ArrayType;
var NPTS : Integer;
var X : X_ArrayType;
var XOUT : XOUT_ArrayType;
var INTPTS : Integer;
var ITYPE : Integer;
var UOUT : UOUT_ArrayType;
var IFAIL : Integer);
stdcall;
external 'nagD03.dll';
{PDEDEF - to define the system of PDEs}
Procedure PDEDEF(var NPDE : Integer;
var T : Double;
var X : Double;
var U : NPDE_ArrayType;
var UX : NPDE_ArrayType;
var P : P_ArrayType;
var Q : NPDE_ArrayType;
var R : NPDE_ArrayType;
var IRES : Integer);
stdcall;
begin
Q[1] := 4.0*ALPHA*(U[2]+X*UX[2]);
Q[2] := 0.0;
R[1] := X*UX[1];
R[2] := UX[2]-U[1]*U[2];
P[1,1] := 0;
P[1,2] := 0;
P[2,1] := 0;
P[2,2] := 1.0-X*X
end;
Procedure BNDARY(var NPDE : Integer;
var T : Double;
var U : NPDE_ArrayType;
var UX : NPDE_ArrayType;
var IBND : Integer;
var BETA : NPDE_ArrayType;
var GAMMA : NPDE_ArrayType;
var IRES : Integer);
stdcall;
begin
if (IBND=0) then
begin
BETA[1] := 0;
BETA[2] := 1;
GAMMA[1] := U[1];
GAMMA[2] := -U[1]*U[2];
end
else
begin
BETA[1] := 1;
BETA[2] := 0;
GAMMA[1] := -U[1];
GAMMA[2] := U[2];
end
end;
Procedure SetUp;
var
I : Integer;
begin
NEQN := NPDE * NPTS;
NIW := NEQN+24;
NWK := (10+6*NPDE)*NEQN;
NW := NWK+(21+3*NPDE)*NPDE+7*NPTS+54;
ACC := 1.0E-4;
M := 1;
ITRACE := 0;
ALPHA := 1.0;
IND := 0;
ITASK := 1;
{Set spatial mesh points}
PIBY2 := 0.5*X01AAF(PI);
HX := PIBY2/(NPTS-1);
X[1] := 0;
X[NPTS] := 1;
for I := 2 to (NPTS-1) Do
begin
X[I] := SIN(HX*(I-1))
end;
{Set initial conditions}
TS := 0.0;
TOUT := 0.1E-4;
end;
{Uinit defines the initial PDE condition}
Procedure Uinit(var U : U_ArrayType;
var X : X_ArrayType;
var NPTS : Integer);
var
I : Integer;
begin
for I := 1 to NPTS Do
begin
U[I,1] := 2.0*ALPHA*X[I];
U[I,2] := 1.0;
end;
end;
begin
WriteLn('D03PCF - Example program results');
SetUp;
WriteLn;
WriteLn('Accuracy requirement = ',ACC);
WriteLn('Parameter alpha = ',ALPHA);
Write(' T / X ');
for I := 1 to 6 Do
Write(XOUT[I] : 6);
WriteLn;
Uinit(U,X,NPTS);
for I := 1 to 5 Do
begin
IFAIL := -1;
TOUT := 10*TOUT;
D03PCF(NPDE,M,TS,TOUT,PDEDEF,BNDARY,U,NPTS,X,ACC,W,NW,IW,N
IW,
ITASK,ITRACE,IND,IFAIL);
D03PZF(NPDE,M,U,NPTS,X,XOUT,INTPTS,ITYPE,UOUT,IFAIL);
WriteLn;
Write(TOUT : 6,' U[1]');
for J := 1 to INTPTS Do
Write(UOUT[1,J,1] : 5,' ');
WriteLn;
Write(' U[2]');
for J := 1 to INTPTS Do
Write(UOUT[1,J,2] : 5,' ');
WriteLn;
end;
WriteLn('Number of integration steps in time',IW[1]);
WriteLn('Number of residual evaluations of resulting ODE
system ',IW[2]);
WriteLn('Number of Jacobian evaluations',IW[3]);
WriteLn('Number of interations of nonlinear solver',IW[5]);
end.
字符串处理与传递
许多的 Fortran DLLs 函数需要输入字符串或是字符。字符串必须要以 null 结束,并且定义为 Pchar 或是字符数组:
strng = array [ 0 . . 2 ] of Char ;
示例程序中我们采用字符数组。请注意,数组起始位置是 0 开始。NAG 的 DLL 函数会预期字符串数组以 0 开始。
函数会要求在字符串传递时提供此字符串的长度。要做到这点最好的方法就是当传递字符串时,紧接着再传递另一个额外的整数参数表明字符串的长度。
例如:
procedure G02EEF(...;
...;
var NAME : Strng_ArrayType;
NAME_Len : Integer;
...;
var NEWVAR : Strng;
NEWVAR_Len : Integer;
...);
stdcall;
external 'nagG02.dll';
调用方式:
G02EEF(..., ..., NAME, 3, ..., NEWVAR, 3, ...);
增加了额外的参数,因为在字符串后会紧跟着整数表明字符串的长度。
Delphi 调用 G02EEF 示例
此示例调用 G02EEF 计算 forward selection 的线性回归模型。我们选择此例子说明传递字符串到 Fortran DLL 的方法。其中也包含多维度数组处理。
unit G02Code;
interface
uses
Forms;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{G02EEF - Example Program in Delphi 2}
type
X_ArrayType = array [1..8, 1..20] of Double;
{X Array, and Q Array below, are defined as the transpose of the parameter
requirements to ensure compatibility with Fortran DLL.}
Strng = array [0..2] of Char;
{A Null terminated string. Note the zero basing of the array
of characters.}
Strng_ArrayType = array [1..8] of Strng;
ISX_ArrayType = array [1..8] of Integer;
WTY_ArrayType = array [1..20] of Double;
EP_ArrayType = array [1..9] of Double;
Q_ArrayType = array [1..10, 1..20] of Double;
WK_ArrayType = array [1..16] of Double;
var
I : Integer;
J : Integer;
NMAX : Integer = 20;
MMAX : Integer = 8;
ISTEP : Integer;
MEAN : Char;
WEIGHT : Char;
N : Integer;
M : Integer;
X : X_ArrayType;
NAME : Strng_ArrayType;
ISX : ISX_ArrayType;
Y : WTY_ArrayType;
WT : WTY_ArrayType;
FIN : Double;
ADDVAR : Boolean;
CHRSS : Double;
F : Double;
MODEL : Strng_ArrayType;
NTERM : Integer;
RSS : Double;
IDF : Integer;
IFR : Integer;
FREE : Strng_ArrayType;
EXSS : EP_ArrayType;
Q : Q_ArrayType;
LDQ : Integer;
P : EP_ArrayType;
WK : WK_ArrayType;
IFAIL : Integer;
NEWVAR : Strng;
Procedure G02EEF(var ISTEP : Integer;
var MEAN : Char;
MEANL : Integer;
var WEIGHT : Char;
WL : Integer;
var N : Integer;
var M : Integer;
var X : X_ArrayType;
var LDX : Integer;
var NAME : Strng_ArrayType;
NAME_L : Integer;
var ISX : ISX_ArrayType;
var MAXIP : Integer;
var Y : WTY_ArrayType;
var WT : WTY_ArrayType;
var FIN : Double;
var ADDVAR : Boolean;
var NEWVAR : Strng;
NVAR_L : Integer;
var CHRSS : Double;
var F : Double;
var MODEL : Strng_ArrayType;
MODL_L : Integer;
var NTERM : Integer;
var RSS : Double;
var IDF : Integer;
var IFR : Integer;
var FREE : Strng_ArrayType;
FREE_L : Integer;
var EXSS : EP_ArrayType;
var Q : Q_ArrayType;
var LDQ : Integer;
var P : EP_ArrayType;
var WK : WK_ArrayType;
var IFAIL : Integer);
stdcall;
external 'nagG02.dll';
Procedure R;
var
Temp : Char;
begin
Read(Temp);
end;
Procedure ReadData;
var
I : Integer;
J : Integer;
begin
ReadLn; {Skip heading in datafile}
Read(N, M);
R; {Skip blank space - See subroutine above}
Read(MEAN,WEIGHT);
If (M<MMAX) and (N<=NMAX) then
begin
for I := 1 to N Do
begin
for J := 1 to M Do
begin
Read(X[J,I]);
end;
Read(Y[I]);
If (WEIGHT='W') or (WEIGHT='w') then
Read(WT[I]);
end;
end;
R;
for J := 1 to M Do
begin
Read(ISX[J]);
end;
R;
for I := 1 to M Do
begin
for J := 0 to 2 Do {note the zero basing of the array and loop}
begin
Read(NAME[I,J]);
end;
R;
end;
Read(FIN);
end;
Procedure FreeVars;
begin
Write('Free variables: ');
for J := 1 to IFR Do
begin
Write(FREE[J]);
Write(' ');
end;
WriteLn;
WriteLn('Change in residual sum of squares for free variables:');
for J := 1 to IFR Do
begin
Write(EXSS[J]);
Write(' ');
end;
WriteLn;
WriteLn;
end;
begin
WriteLn('G02EEF Example Program Results');
ISTEP := 0;
IFAIL := 0;
ReadData;
for I:=1 to M Do
begin
IFAIL:=0;
G02EEF(ISTEP,MEAN,1,WEIGHT,1,N,M,X,NMAX,NAME,3,ISX,MMAX,Y,WT,
FIN,ADDVAR,NEWVAR,3,CHRSS,F,MODEL,3,NTERM,RSS,IDF,
IFR,FREE,3,EXSS,Q,NMAX,P,WK,IFAIL);
{Fortran DLL 需要知道传递字符串的长度。因此,需要在每个字符串后接着提供字符串长度的整数值。}
if (IFAIL<>0) then
begin
WriteLn('IFAIL = ',IFAIL);
Exit;
end;
WriteLn;
WriteLn('Step ',ISTEP);
if (ADDVAR<>TRUE) then
begin
WriteLn('No further variables added maximum F =',F);
FreeVars;
Exit;
end
else
begin
WriteLn('Added variable is ',NEWVAR);
WriteLn('Change in residual sum of squares =',CHRSS);
WriteLn('F Statistic = ',F);
WriteLn;
Write('Variables in model: ');
for J := 1 to NTERM Do
begin
Write(MODEL[J]);
Write(' ');
end;
WriteLn;
WriteLn;
WriteLn('Residual sum of squares = ',RSS);
WriteLn('Degrees of freedom = ',IDF);
WriteLn;
if (IFR=0) then
begin
WriteLn('No free variables remaining');
Exit;
end;
FreeVars;
end;
end;
end.