Civil Engineer's Forum-FORUM8
このページをスタートページに設定する QR

Facebook - FORUM8

Twitter - FORUM8

YouTube - FORUM8

 サイトマップ | ご利用条件 | お問い合わせ | 英語翻訳サイト | Japanese | English | Korean | CHS/CHT | Vietnam | Francais | Europe

3
 構造解析プログラミング講座(2)
 
前回の誌上セミナーでは、構造解析プログラミングの基本に加えて、FRAME面内SDKの概要と活用方法について解説しました。続く今回は、FRAME面内SDKが提供する関数を使用して実際に計算条件を入力し、計算結果をテキストファイルに出力するまでのサンプルプログラムについて解説します。
 サンプルプログラムの構成

 FRAME面内SDKのサンプルプログラムは、以下のファイルで構成されています。なお、このサンプルプログラムの実行には、FRAME面内SDKに同梱の「FrameSDK.DLL」「FrameSDKHeader.pas」の2ファイルが別途必要になります。

 表1 FRAME面内SDK サンプルファイルの構成
FrameSample.dpr サンプルプログラムのプロジェクトファイル
FileUtil.pas ファイル入出力クラス
InputDataSample.txt 入力データのサンプル
OutputSample.F11〜.F71 計算結果出力のサンプル
SampleMain.pas メインフォーム(ソースコード部)
SampleMain.dfm メインフォーム(ソースコード部)


 使用する関数とインポート方法

 FRAME面内SDKが提供する関数は、以下の3つのみです。また、実際に計算機能を提供するのはFrameCalc関数1つだけになります。FRAME面内SDK利用の流れとしては、まずFR_SDK_MAINDATA型構造体に入力条件をセットしていき、FrameCalc関数に構造体のポインタを渡します。計算結果の取得はPointer型の戻り値から逆参照する形で行います。

 表2 FRAME面内SDKの提供する関数
FrameCalc関数 面内解析計算の実行および実行結果を返す。
FrameResMemFree関数 計算結果領域のメモリを開放する。
通常は、FrameCalc関数の後に呼び出す。
FrameErr関数 FrameCalc関数の計算中に発生したエラーの詳細情報を取得。

 今回のサンプルプログラムではFrameSDKHeader.pas内で一括でインポートされているので、そこでの記述について簡単な解説を行います。インポート部の記述を単一のファイルにまとめてしまえば、別アプリケーションで同様の機能を利用する際に、DLLとセットでこのpasファイルをuses節に追加するだけでよいので、作業が容易になります。

FrameSDKHeader.pas
    unit FrameSDKHeader;
      :
       (中略)
       :
     implementation
    
     // Frame Calc
    function FrameCalc ( lpszFramePath : PChar;
 lpstAgInData : PFR_SDK_MAINDATA ) : Pointer; stdcall;

     external 'FrameSDK.dll';
   
     // Free the Result Data Area
 procedure FrameResMemFree ( lpstAgOutData : Pointer ) ;
 stdcall;
     external 'FrameSDK.dll';
   
     // Get Error Information
 function FrameErr ( lpErrMsg : PPChar ) : Integer ;
 stdcall;
     external 'FrameSDK.dll';
   
 end;

 上記は前述の3つの関数を、FrameSDK.dllからインポートしている例ですが、通常アプリケーション内で関数を宣言する際とは、"stdcall"と"external"という記述がある点で異なっています。stdcallとは呼び出し規約と呼ばれるもので、パラメータをルーチンに渡す順序や例外処理の方法などを指定しています。external宣言は外部ルーチン(関数)を呼び出すオブジェクトファイルまたはライブラリ名を指定しています。


 サンプルプログラムの画面構成

 サンプルプログラムの画面構成は下図のようになっています。イベントは各TButtonコンポーネントに対し設定されています。


(1)ButtonFrameDir TButton (6)EditFileOut TEdit
(2)EditFrameDir TEdit (7)ButtonCalc TButton
(3)ButtonFileIn TButton (8)OpenDialogFrameDir TOpenDialog
(4)EditFileIn TEdit (9)OpenDialogFileIn TOpenDialog
(5)ButtonFileOut TButton (10)SaveDialogFileOut TSaveDialog
図1 FRAME面内SDKの提供する関数

 (1)〜(5)までのボタンについては、クリック時にOpenDialogまたはSaveDialogを実行し、それぞれFRAME面内本体のインストールフォルダ、入力データのファイル名、計算結果の保存先ファイル名を指定させ、確定時に隣接する(2)〜(6)までのEditに値を代入しています。



 入力データファイルの内容

 ここでは、サンプルプログラムで使用する入力データファイルの大まかな書式と、読み込みに使用するDelphiのクラスについて説明します。

図2 入力データモデル例(BOXカルバート、構造図・荷重・支点条件)

入力データ例(inputDataSample.txt)
    FRAME-02
    TITLE     FrameSDKSample
    CONTROL1   格点 部材 材質 断面 バC バネ 支C 支点
                  6   7   1   2   1   0   1   0
    CONTROL2   荷重 組合 抽出 二重 剛S 剛B
                  3   2   1   0   0   0
    CONTROL3   リナ Mmax 着点 割増 プレ
                  1   0   0   0   0
    POINT         6
             1    0.0000    4.0000
             2    5.0000    4.0000
             3   10.0000    4.0000
             4    0.0000    0.0000
             5    5.0000    0.0000
             6   10.0000    0.0000
              :
            (中略)
              :
    END

 入力データファイルの中身は、まず開始カード(FRAME-02)から始まり、タイトル(TITLE)、コントロールデータ(CONTROL1〜3)、座標データ(POINT)、材質データ、断面データというように、値が順番に定義されています。値と値との間は半角スペースで区切られています。これを1行ずつ読み込み、行を列に分割するという流れで値を読み込んでいく際に使うのが、Textfile変数とTStringListクラスです。
 Textfile変数はファイルをメモリ上に取り込んで1行ずつ読み込むのに使用し、行をさらに列単位に分割するのにTStringListを利用します。
具体的な利用方法については、実際に以下のコードを見ながら説明していきます。

TFrameFileClass.LoadFromFile(FileUtils.pas)
57   function TFrameFileClass.LoadFromFile( FileName:string;
         var FrameData: FR_SDK_MAINDATA ):boolean;
58      var
59        i : integer;
60        j : Integer;
61        txtfile : TextFile;
62        Line    : String;
63        Title   : String;
64        Fields  : TStringList;
65      begin
66        Result  := False;
67        Fields := TStringList.Create;
68        AssignFile( txtfile, FileName );
69        Reset( txtfile );
70        try
71          // FRAME-02
72          Readln( txtfile, Line );
73   
74          // TITLE
75          Readln( txtfile, Line );
76          Title := Copy( Line, 11, 40 );
77    StrCopy( FrameData.aszCalcTitle, PAnsiChar(Title) );
:              :
:            (中略)
:              :
109          // POINT
110          Readln( txtfile, Line );
111          SetLength( NodeData, FrameData.nNodeCnt );
112          for i := 0 to FrameData.nNodeCnt-1 do
113            begin
114              Readln( txtfile, Line );
115              Fields.CommaText := Line;
116       NodeData[i].nNodeNo := StrToInt(Fields.Strings[0]);
117       NodeData[i].dbXPoint :=
          StrToFloatDef(Fields.Strings[1], 0.000 );
118       NodeData[i].dbYPoint :=
          StrToFloatDef(Fields.Strings[2], 0.000 );
119            end;
120          FrameData.lpNode := @NodeData[0];
:              :
:            (中略)
:              :
330        Result := True;
331    finally
332    CloseFile( txtfile );
333    Fields.Free;
334    end;
335    end;


LoadFromFile関数内で定義される変数
変数名 用途
i, j Integer ループ変数
txtfile TextFile 入力データファイルを行ごとに読み込むために使用
Line AnsiString 1行分のデータを格納するための文字列
Title AnsiString タイトルを格納するための文字列
Fields TStringList Lineを分割するために使用

 LoadFromFile関数内では以下の変数を定義しています。
 これらの変数は、入力データファイルの「FRAME-02」で始まる開始カードから「END」で終わる終了カードまでの読み込み中に共通で使用します。

各行の解説
 先ほどのコードの中から、タイトルデータ、座標データを例として取り上げ、処理の内容について説明します。
 まず、68〜69行目にかけてAssignFile手続きでtxtfile変数と入力データファイルとを関連付けています。これ以降のtxtfile変数に対しての操作は、すべてFileNameで指定された入力データファイル上で行われます。次にReset手続きでファイルを開いています。このとき、ファイルの位置はファイルの先頭となります。73行目からファイルを1行ずつ読み取っていきますが、このときにReadln手続きを使用しています。

       Readln( txtfile, Line );

 Readln手続きを呼び出すと、第2引数に指定した文字列変数(この場合Line)に現在の位置からの1行分のテキスト行が格納されます。
ファイル位置が次の行の先頭に移動しているので、次にReadln手続きを呼び出した際には、Line変数には次の行のデータが格納されます。
このように、行の取り出しはReadln手続きのみを使用していきます。
 続いてタイトルデータを読み込みます。入力ファイルのフォーマット上で行の11文字目〜50文字目まで(40文字)をタイトルとしているため、Copy関数を使って11文字目から40文字を読み込んでいます。

      Title := Copy( Line, 11, 40 );

 次に座標データを読み込みます。まず110行目でReadln手続きを実行した時点で、Lineには"POINT  6"という値が格納されています。
このうち"6"という数値については格点数を示していますが、格点数については座標データよりも前に入力データファイルの3〜4行目にあるコントロールデータの行で既に定義されているので、サンプルデータではその情報を使用しており、ここではそのまま読み飛ばしています。

      SetLength( NodeData, FrameData.nNodeCnt );

 さらに、SetLength関数で動的配列NodeDataの要素数(長さ)を格点数分割り当てていますが、NodeDataについてはTFrameFileClassのprivate部で次のように宣言しています。

      NodeData         : array of FR_SDK_NODE;

 NodeDataはFR_SDK_NODE型構造体の動的配列として宣言しています。構造体のサイズを指定したところで、119行目から実際にfor文で格点数分をループし、構造体に格点情報をセットしていきます。

     for i := 0 to FrameData.nNodeCnt-1 do
       begin          Readln( txtfile, Line );
         Fields.CommaText := Line;
       NodeData[i].nNodeNo  := StrToInt(Fields.Strings[0]);
      NodeData[i].dbXPoint := StrToFloat(Fields.Strings[1]);
      NodeData[i].dbYPoint := StrToFloat(Fields.Strings[2]);
       end;
     FrameData.lpNode := @NodeData[0];

 ここでもReadln手続きにより、1行分の文字列をLine変数に格納します。この時点で、Line変数には以下のような値が格納されます。

121行目のReadlnの実行結果
    Line = "         1    0.0000    4.0000"

  この時点ではLine変数に格納されている値は単一の文字列に過ぎません。これを「1」「0.0000」「4.0000」の3つの値に分解するには、TStringListクラスのCommaTextプロパティを利用します。CommaTextプロパティに文字列を代入すると、スペースや","(カンマ)などを区切り文字として自動的にStringsというString型の配列プロパティにセットされます。

122行目のCommaTextの代入結果
    Fields.Strings[0] = "1"
    Fields.Strings[1] = "0.0000"
    Fields.Strings[2] = "4.0000"

 これ以降は、FieldsのStringsプロパティに格納された文字列の値を、StrToInt関数やStrToFloat関数などを使用して数値型にキャストしたうえで、NodeDataレコードのメンバ変数にセットし、NodeDataレコードをFrameDataレコードに格納して、格点データの読み込みは終わりとなります。


 計算結果をファイルへ出力する

 FrameCalc関数から受け取った計算結果をテキストファイルに出力する例について説明します。
 ここでは、入力データファイルの読み込みとは対照的に、ポインタの値を参照してテキストファイルに書き出していきます。
以下のような書式で格点変位(基本荷重ケース)の計算結果をファイルに出力するまでの処理を取り上げて解説します。

格点変位(基本荷重ケース)の計算結果出力例
     格点変位
 CASE       1          6
 1         -0.013         -1.071         -0.186
 2         -0.049         -0.963          0.000
 3         -0.085         -1.071          0.186
 4          0.000         -1.016          0.197
 5         -0.049         -0.842          0.000
 6         -0.099         -1.016         -0.197
     格点変位
 CASE       2          6
 1         -0.109         -0.259         -0.083
 2         -0.110         -0.133          0.089
 3         -0.110          0.015          0.032
 4          0.000         -0.247          0.112
 5          0.001         -0.113         -0.004
 6          0.001          0.016          0.019
 :              :
 :            (後略)

 サンプルプログラムでは、出力するファイルごとに関数が分けられており、本節で取り扱う格点変位の計算結果出力は、TFrameFileClassクラス内のResultSetF21ToFile手続き内で行っています。FrameResDataはFrameCalc関数からの計算結果を格納するPFR_SDK_MAINRES型のポインタ変数で、クラスのprivate節で定義しています。

TFrameFileClass.ResultSetF21ToFile(FileUtil.pas)
 procedure TFrameFileClass.ResultSetF21ToFile;
   var
     i, j       : Integer;
     outputSL   : TStringList;
     F21DIS     : PFR_SDK_F21DIS;
     F21NodeDis : PFR_SDK_NODEDIS;
   begin
     outputSL := TStringList.Create;
     F21DIS   := FrameResData.lpF21Dis;
     try
       for i := 0 to FrameResData.nF21DisCnt-1 do
         begin
           outputSL.Add( '格点変位' );
           outputSL.Add( Format( 'CASE  %4d %4d',
     [F21DIS^.nFndmntlLdCaseNo, F21DIS^.nNodeCnt ] ) );
           F21NodeDis := F21DIS^.lpNodeDis;
           for j := 0 to F21DIS^.nNodeCnt-1 do
             begin
         outputSL.Add( Format( '%5d %14.3f %14.3f %14.3f',
                               [ F21NodeDis^.nNodeNo,
                            F21NodeDis^.dbHorzDsplcmnt,
                        F21NodeDis^.dbVertDsplcmnt,
                   F21NodeDis^.dbRevDsplcmnt ] ) );
           Inc( F21NodeDis );
             end;
           Inc( F21DIS );
          end;
 outputSL.SaveToFile( ChangeFileExt( FFileName, '.F21' ) );
     finally
       outputSL.Free;
       end;
   end;

 ポインタであるFrameResDataの示す先には、レコード型のデータが格納されており、格点変位の計算結果はこのレコード内のlpF21Disというレコード型のポインタを参照することで取得できます。ここでのポイントは、FrameResDataからは格点データのアドレスのみを受け取り、実際に格点数分のデータを参照する際にはF21DISという同じ型の別のポインタ変数を使用しているという点です。
 最初に、F21DISにFrameResData内の格点変位データのポインタを代入しています。この時点でF21DISの参照先には1ケース目の荷重ケースの格点情報が格納されます。F21DISには荷重ケースNoと格点数といったヘッダ情報が含まれており、格点変位はF21DISの参照レコード内にあるポインタlpNodeDisの参照先に格納されています。ここでも同じ型のポインタ変数F21NodeDisを用意して、444行目にてlpNodeDisのポインタを代入しています。
 この時点でF21NodeDisには、荷重ケース1の1点目の格点変位情報が格納されています。後は、for文で格点数分でループさせて、F21NodeDisから荷重ケース1の全格点数分の格点変位を参照していけばよいことになりますが、F21NodeDisは配列型のポインタではありません。
 では、どのようにして残りの格点の計算結果を参照するのでしょうか。

              Inc( F21NodeDis );

 forループ内では、ポインタF21NodeDisに対してInc手続きを行っています。Inc手続きは、数値を加算(インクリメント)するのによく使用しますが、引数にポインタを指定した場合はポインタの位置がポインタの型のサイズ分移動します。F21NodeDisには格点数分のレコード用のメモリ領域が確保されており、Inc手続きを行うことでポインタの位置が次の要素の先頭に移動しています。これによって、Inc手続き後にF21NodeDisを参照した際には、次の格点の変位情報を取得しています。
 さらに、ポインタからレコードのメンバ変数の値を取り出し、Format関数を使って書式を整えた上で1行ずつ、TStringListのインスタンスクラスであるOutputSLに格納していきます。1行ずつの書き出しには、TStringlistクラスのAdd関数を使用し、最後に同じくTStringListクラスのSaveToFile関数を使用してファイルに出力します。

 次回の誌上セミナーは、「構造解析プログラミング講座(3)」になります。建築構造解析(Mutiframe)のオートメーションを利用したDelphiプログラムの開発について解説する予定です。引き続きご期待ください。


有償セミナーのお知らせ
 エンジニアのプログラミング入門セミナー  CPD認定6.2pt
 日時 2011年 6月3日(金) 9:30〜16:30
 受講費 1名様 15,750円(税込)
 本会場 フォーラムエイト東京本社 GTタワーセミナールーム
TV会議システムにて東京・大阪・名古屋・福岡にて同時開催


     
前ページ    インデックス    次ページ
(Up&Coming '11 新緑の号掲載)
戻る
Up&Coming

FORUM8


お問合せ窓口




[ ユーザー紹介 ]
櫻井工業株式会社
株式会社TEAM IWAKIRI PRODUCTS
[ 開催報告 ]
第17回FORUM8デザインフェスティバル






>> 製品総合カタログ


>> プレミアム会員サービス
>> ファイナンシャルサポート

最近リリースした製品
3次元鋼管矢板基礎の設計計算(部分係数法・H29道示対応) Ver.4
UC-1・UC-1 Cloud 統合版 BOXカルバート
電子納品支援ツール Ver.17
柔構造樋門の設計・3D配筋 Ver.17
建築杭基礎の設計計算 Ver.10

キャンペーン実施中
地方創生・国土強靭化 FORUM8セミナーフェアキャンペーン
レンタルアクセス同時購入キャンペーン・過去レンタルキャンペーン
Shade3D・F8VPS 20%OFFキャンペーン

セミナー・イベントカレンダー
開催間近のセミナー
4/2  ブロックUIプログラ
  ミングツールで学ぶ
  ジュニア・プログラミング
4/3  動的解析
4/4  下水道耐震設計体験
4/5  Shade3D-F8VPS
  メタバース入門

ソフトウェア/支援サービス
VRソフト(バーチャルリアリティ)
《UC-winシリーズ》
・道路・シミュレータ
・ドライブ・シミュレータ
・マイクロ・シミュレーション
・避難解析・シミュレーション
>>その他VRソフト
FEM解析ソフト
・3次元プレート動的非線形解析
・2次元動的非線形解析
・総合有限要素法解析システム
>>その他FEM解析ソフト
土木・建築・設計ソフト
《UC-1シリーズ》
・構造解析/断面
・橋梁上部工
・橋梁下部工
・基礎工
・仮設工
・道路土工
・港湾
・水工
・地盤解析
・CAD/CIM、建設会計
・維持管理・地震リスク
・建築
・船舶/避難
>>その他土木・建築・設計ソフト
クラウド
《スパコンクラウド®》
・スパコンクラウドサービス
《VR-Cloud®》
・リアルタイムVRシステム
解析支援サービス/サポート
・UC-win/Roadサポートシステム
・設計成果チェック支援サービス
・Engineer's Studio®解析支援
・地盤解析支援サービス
・EXODUS/SMARTFIRE解析支援
・xpswmm解析支援サービス
・建物エネルギーシミュレーション
・3Dレーザスキャン・モデリング
・3D模型サービス
・3D報告書・図面サービス
>>その他支援サービス
各種ソリューション
・耐震診断/解析
・鋼橋設計
・橋梁新工法
・建築設計
・自治体
・医療系VRシステム
・パーキングソリューション
・ECOソリューション
>>その他ソリューション