DelphiからnAG Fortran ライブラリDLLを呼び出す際の1つの注意点として、実引数がvar型である必要がある点があげられます。これはFortranの呼び出し規約が引数の参照渡しを必要としているからです。DelphiではnAG Fortran ライブラリDLL内のルーチンを直接コードないから呼び出すことによりコンパイラが自動的にリンクしてくれます。そのためコンパイル時のリンクリストにnAGライブラリを指定する必要はありません。
nAG Fortran ライブラリDLLへの参照は、Delphiではexternalなprocedureもしくはfunctionとして定義されます。ここでのprocedureはDLL内のルーチンと同じ名前にする必要があります。Delphiは大文字小文字の区別をするため、nAG FortranライブラリDLLルーチン名は大文字で指定してください。(Delphiのname constructを使って変更することは可能です)
以下の例をまずご覧ください:
function S18AEF(var X : Double; var IFAIL : Integer): Double; stdcall; external 'nagsx.dll';
stdcall指示子の指定を行ってください。nAG Fortran ライブラリDLLはこの呼び出し規約で呼び出す必要があります。この指定によりnAG FortranライブラリDLL内の関数もしくはサブルーチンは通常のfunctionもしくはprocedureとして呼び出すことが可能です。以下はその一例です。
WriteLn(S18AEF(X, IFAIL))
多次元配列
2次元以上の配列は転置する必要があります。これはnAG FortranライブラリDLLがFortranの配列順序(colamn major)を受けるからです。例えばA[2,2]はメモリ上では A[1,1], A[2,1], A[1,2], A[2,2]の順序で保持されます。Pascalではこの列と行の順序が逆(row major)になり、メモリ上ではA[1,1], A[1,2], A[2,1], A[2,2]の順序で保持されます。Pascalの配列は実引数としてnAG FortranライブラリのDLLに渡されるので、下記の「D03PCF Example」で示されるように、データタイプとして定義する必要があります。varセクションで定義されるPascal変数配列が実引数として渡されると他のパラメータ値を上書きしてしまいます。 多次元配列の扱いについては「D03PCF Example」をご参照ください。
関数と手続きのを渡す
一部のnAGライブラリルーチンは、関数もしくはサブルーチンを引数として受け取ります。これをDelphiで行う場合には引数として渡すprocedureもしくはfunctionをtype headingで定義される1つのデータタイプとして指定する必要があります。これによりパラメータリストとしてDLLに渡すことが可能になります。型定義においてサブルーチンが持つ引数の数と型が一致している必要があります。ここでvarは必要ないことに注意して下さい。これは引き渡しの際に1つのコピーしか必要とされないからです。またstdcallの指定がfunction/procedure定義とデータ型定義の両方に必要である点にも注意して下さい。
DelphiによるD03PCF Example
以下の例はnAG FortranライブラリDLL内のルーチンD03PCFを呼び出すものです。このルーチンは線形もしくは非線形の連立PDEを解くものです。下記のプログラムでは多次元配列の使用方法と引数としての関数の渡し方が示されます。更に外部関数としてX01AAFを用いてπを得ています。
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; {Note: the two arrays above are defined as the transpose of the parameter requirements to ensure compatibility with Fortran DLLs.} 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; {The two types above are Procedure types. } 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.
文字列の扱い、および渡し方
いくつかのnAG Fortran ライブラリ内のルーチンは文字もしくは文字列を引数として受け取ります。文字列はnullで終端している必要があります。文字列はPcharもしくは以下のように文字配列として定義して下さい。
strng = array [ 0 . . 2 ] of Char ;
以下の例では文字列配列を使います。配列は0ベース(0から始まる)である必要があります。nAG Fortran ライブラリDLLは0ベース以外の配列ではエラーになります。
またnAG Fortran ライブラリDLLは文字列引数の直後に文字列長を受け取ります。そのため文字列の次にintegerパラメータで文字列長を渡して下さい。以下はその例です。
procedure G02EEF(...; ...; var NAME : Strng_ArrayType; NAME_Len : Integer; ...; var NEWVAR : Strng; NEWVAR_Len : Integer; ...); stdcall; external 'nagG02.dll';
そしてその呼び出し方法です。
G02EEF(..., ..., NAME, 3, ..., NEWVAR, 3, ...);
これらの文字列長の引数はcharacterやStrng_ArrayTypeなどの文字列配列の後に必要です。
DelphiによるG02EEF Example
下記の例はnAG FortranライブラリルーチンのG02EEFを用いて前方選択手続により最適な線形回帰モデルを見つけ出すものです。この例では文字列を渡す際の問題と多次元配列の扱いが示されます。
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); {NB Fortran requires the length of the strings to be passed immediately following the strings themselves. Therefore it expects an integer after every string parameter.} 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.