Calling TRNSYS Functions from C/C++

TRNSYSの関数をC/C++から呼び出す方法について紹介します。というか備忘です。
In this article I will explain how to call TRNSYS functions from C/C++.

1. 引数/argument

C/C++では引数は下表のように置き換える必要があります。
In the C/C++ language, the argument needs to be replaced as shown in the table below.

 

FORTRAN C/C++
Integer int*
Real(8),
Double Precision
double*
Character(Len=n) char*, size_t n

コーディング例

1.1. Integer

– TRNSYS.h

extern “C” __declspec(dllimport) void _cdecl SETTYPEVERSION(int* ver);
#define setTypeVersion                 SETTYPEVERSION

– Source code

if (getIsVersionSigningTime())
{
     int ver = 17;
     setTypeVersion(&ver);
     return 1;
}

1.2. Double Precision

– TRNSYS.h

extern “C” __declspec(dllimport) double _cdecl TRNSYSFUNCTIONS_mp_GETINPUTVALUE(int* n);
#define getInputValue                   TRNSYSFUNCTIONS_mp_GETINPUTVALUE

– Source code

int no = 1;
double ret = getInputValue(&no);

1.2. Character

文字列を渡す引数では、size_t(文字列の長さ)を引数の最後に加える。
NOTE: For string argument, the length(size_t) has to be added at the end of the arguments.

– TRNSYS.h

extern “C” __declspec(dllimport) void _cdecl SETINPUTUNITS(int*i, char* string, size_t len);
#define setInputUnits                   SETINPUTUNITS

– Source code

int i = 1;
setInputUnits(&i, “DM1”, 3);  

2. 戻り値/return value

戻り値は下表のように置き換えます。
In the C/C++ language, the return value needs to be replaced as shown below.

 

FORTRAN C/C++
Logical int
Integer int
Real(8),
Double Precision
double
Character(Len=n) char* , size_t n

コーデング例

2.1. Logical

– TRNSYS.h

extern “C” __declspec(dllimport) int    _cdecl TRNSYSFUNCTIONS_mp_ERRORFOUND(void);
#define ErrorFound                     TRNSYSFUNCTIONS_mp_ERRORFOUND  

– Source code

if (ErrorFound()) return 1; // 0:false, except 0: true in C/C++

2.2. Integer

– TRNSYS.h

extern “C” __declspec(dllimport) int _cdecl TRNSYSFUNCTIONS_mp_GETMAXLABELLENGTH(void);
#define getMaxLabelLength        TRNSYSFUNCTIONS_mp_GETMAXLABELLENGTH  

– Source code

size_t maxlen = getMaxLabelLength();

2.3. Double Precision

– TRNSYS.h

extern “C” __declspec(dllimport) double _cdecl TRNSYSFUNCTIONS_mp_GETINPUTVALUE(int* n);
#define getInputValue                TRNSYSFUNCTIONS_mp_GETINPUTVALUE

– Source code

int no = 1;
double ret = getInputValue(&no);

2.4 Character

※文字列(Character型)を返す関数では、「引数」としてchar* と size_t(文字列の長さ)を第1、第2引数として指定する。
NOTE: For a function which returns ‘Character’, char* and size_t has to be needed as the first and second arguments in C/C++.

– TRNSYS.h

extern “C” __declspec(dllimport) int  _cdecl TRNSYSFUNCTIONS_mp_GETMAXPATHLENGTH(void);
extern “C” __declspec(dllimport) char*  _cdecl TRNSYSFUNCTIONS_mp_GETDECKFILENAME(char* dck, size_t len);

#define getMaxPathLength         TRNSYSFUNCTIONS_mp_GETMAXPATHLENGTH 
#define getDeckFileName           TRNSYSFUNCTIONS_mp_GETDECKFILENAME

– Source code

size_t maxlen = getMaxPathLength();
char *fname= new char[maxlen];
getDeckFileName(fname, maxlen);
std::string deckFileName = trim(std::string(fname,0,maxlen)); // trimming the string, just in case
delete[] fname; // Trimming the string
std::string trim(const std::string& str)
{
    size_t first = str.find_first_not_of(‘ ‘);
    if (std::string::npos == first)
    {
        return str;
    }
    size_t last = str.find_last_not_of(‘ ‘);
    return str.substr(first, (last – first + 1));
}

Intel FORTRANのサブルーチンをC/C++から呼び出す

Intel FORTRANのサブルーチンをC/C++から呼び出す方法について調べてみた。

ググってみたら、あっさり出てきたのでメモしておきます。

Standard Fortran and C Interoperability

CとFORTRAN相互の呼び出しの説明があるのですが、CからFORTRANを呼び出す場合は。。。

The following example calls a Fortran subroutine called Simulation. This subroutine corresponds to the C void function simulation.

ということで、サブルーチンはC/C++からは戻り値なし(void型)の関数として呼び出せるようです。

引数に関しては、FORTRAN側の宣言により値だったり、ポインタだったり変わるのか(引数にvalueが指定されていると値型?)。。。
FORTRANって、まだまだ知らない構文が一杯あるな。。。

FORTRANのサンプルって探すの大変なんですよね。

ちと必要に迫られてFORTRANのサンプルを探してみる。
調べてみると数値計算で使う関数って製品化されているものや、オープンソースのものなどいろいろあるようです。
ちょっと時間があれば詳しく見てみたいところではあるんですが、導入にはそれなりに手間が掛かる。
ちょっとした計算だと、大規模なライブラリを導入するのも面倒。有料だと配布もしにくいし。

あっちこっち探していたら、それっぽいサイトを見つけたので忘れないようにメモ。

FORTRAN77 Source Codes
http://people.sc.fsu.edu/~jburkardt/f77_src/f77_src.html

コンポーネントが認識されない。。。(2)

さて、前回、新しく作ったコンポーネントがTRNSYSから認識されない状況に陥りました。

原因は参照しているMSVCR110.DLLが見つけられないこと。

このファイルが何かというと、一般には「Cランタイムライブラリ」と言われるファイルです。コンポーネントはFORTRANで書いているので、なんでCのランタイム参照してるんだという気もしますが、まあ、必要なんで参照してます。

単純な話、このMSVCR110.DLLをTRNSYSから認識できるパスに放り込んでやればエラーはでなくなります。

例)VS2012では以下のフォルダに格納されています。

C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\redist\x86\Microsoft.VC110.CRT

このフォルダから、DLLをC:\TRNSYS17\Exeフォルダにコピーします。これであっさり解決です。

落とし穴

と、ここで落とし穴が待ち構えています。この「Cランタイムライブラリ」、TRNSYS自身も参照しています。しかも厄介なことに、少し前のバージョンを参照していてファイル名も違っています。(具体的にはMSVCRT.DLLという名前のファイル)

図にすると、こんな感じ。

ここで何が問題かというと、TRNSYS本体とコンポーネントの間で参照先が違うので、実行時に齟齬が発生するケースがあります。(大部分は問題ないのですが、一部の処理で齟齬が出ます)

なんちゅーか、初版と改訂版のテキスト混在みたいな感じですかね。同じ授業なのに人によって使っているテキスト違っているから、受講者の理解している内容が違っているみたいな。ちょっと違うか。

具体的な例としては、データファイルを参照しているコンポーネントだとTRNSYS本体が割り当てたLUがコンポーネント側から見えないといった問題となって現れます。TRNSYSのデータとしては問題ないのに、エラーで動かない状況になります。

こういう時は基本的に新しい方に合わせてしまうのがよろしい。という事でTRNSYS(TRNDLL.DLL)も同じライブラリを参照するようにビルドし直してやればOK.

図にすると、こんどは、こんな感じ。

同じライブラリを参照するようになるので、齟齬もなく安心して使えます。

実作業としてはTRNDLL.DLLを開発中のコンポーネントと同じコンパイラで再ビルドすればOKです。

FORTRANでWindowsイベント処理

計算型のコンポーネントを作ったら。。。

条件が成立するまでWaitと計算を繰り返すコンポーネントを作ったら、止まらなくなりました。

いや、いずれ条件が成り立てば止まるんですが、途中で止めたくても止まらなくなってしまいました。

具体的にどうなるかというと。。。

計算が全く進まない状態。条件の成立を待ちをしているので、これはこれで正常な状態。

でも途中で止めたくなって「Abort」ボタンをクリックすると。。。。

これが、本格的に問題があるようなメッセージになってしまう。とほほっ。。。

これはなんか格好が悪い

これって、Windowsのイベント処理の問題というか、コンポーネントが計算に夢中になって処理を返さないのが原因。VBなんかでも計算型のプログラムを書くと陥りやすいパターンです。VBにはDoEvents()っていう便利な関数が用意されているので、適当なタイミング、一般的には時間の掛かりそうなループの途中で呼んであげれば簡単に解決できます。

しかし、今回はFORTRAN。そんな便利な関数はない。というか、言語仕様にそんな機能があったら変だ。

だが、そこはIntel FORTRANのこと、なんか用意してそうだと思ったら、やっぱりありました。Win32 APIを直接呼び出しているサンプルが!

ということで、DoEvents()もどきのサブルーチンに仕立ててみました。

こいつを適宜呼び出すことで無事に解決。

! 呼出し側   

! 計算の途中でイベントの処理を流す   

Call DoEvents() !Windowsイベントを処理する

!———————————————————–
! イベント処理   
!———————————————————–
SUBROUTINE DoEvents()
use IFWINTY
use USER32
use IFLOGM
logical lNotQuit, lret
integer iret
TYPE (T_MSG) mesg
lNotQuit = .TRUE.
do while (lNotQuit .AND. (PeekMessage(mesg, 0, 0, 0, PM_NOREMOVE) /= 0))
  lNotQuit = GetMessage(mesg, NULL, 0, 0)
    if (lNotQuit) then
      if (DLGISDLGMESSAGE(mesg) .EQV. .FALSE.) then
         lret = TranslateMessage(mesg)
         iret = DispatchMessage(mesg)
      end if
    end if
end do
END SUBROUTINE DoEvents

No single-threaded DLL option

TRNSYS-Usersを見ていたら、FORTRANコンパイラの設定の質問が流れていました。

[TRNSYS-users] No single-threaded DLL option

Type29をビルドしなおしてみたら、コンポーネントからのファイル作成時にエラーになってしまうようです。現象としては”fort.xxx”というファイルの書出しに失敗するというもの。

対策としてコンパイラの設定を変更しようとしたけど、該当項目がない。しょうがないのでWindowsXpとCompaq Fortranで対応したどWindows7の環境で何とかならんの?という質問。

これと全く同じ話で最近悩んだので、以下、備忘のため書いておきます。

質問者のメールには書いてないけど、この件の対策の元ネタはTRNSYSのドキュメントの以下の記載。

7.5.2.6. Troubleshooting multiple DLL’s

ここに原因と対策が記載されているんですが、悩んだのが対策の記述。

set “Project Settings\Fortran\Libraries” to “Single-threaded DLL” in the Visual Studio

Visual Studioでライブラリーの設定を変更しろとありますが、Intel Visual Fortran(以下、IVF)のライブラリーの設定に”Single-threaded DLL”って無いんです。

でも、IVFの場合はここで、単に”Mutli-threaded DLL”を選べばOK。エラーの原因になるのはSingleかMultiかの違いじゃなくて、ライブラリーをDLLにしているかStaticにしているかの違いです。

ということで”DLL”の方を選べばOKです。

実際の設定画面のキャプチャーを載せておきます。

  • Release
  • Debug

ただ注意しないといけないのは、TRNDLL側も同じ設定にしてビルドしないと、やっぱりエラーになります。

Intel Fortranで固定形式と自由形式の切り替え方法

FORTRANのソースコードの形式には固定形式と自由形式があります。前者はいわゆるFortran77互換の形式、後者はFortran90です。

FORTRANのソースコードの形式には固定形式と自由形式があります。前者はいわゆるFortran77互換の形式、後者はFortran90です。


Intel Fortranではどちらの形式にも対応していて、デフォルトではファイル拡張子で区別しているようです。


拡張子による判定

固定形式(Fortran77):*.for、*.f

自由形式(Fortran90):*.f90

TRNSYSが書きだすコンポーネントの雛形は拡張子*.forで書きだされます。つまり、Fortran77形式ですね。最初、この切替方法に気づかずに悩んでしまいました。

あるソースコードではコンパイルできるのに、同じ書き方を別のソースコードで書くとエラーになる。例えば、一行72文字を超えたコードがソースコードによってビルドできたり、できなかったりします。

新旧のソースコードが混在するときは、拡張子で分けてくれるので重宝しますが、それに気が付かないと、あれっ?ってな感じになります。


ちなみに一括してどちらかの形式を指定したい場合は、プロパティダイアログの以下の項目で設定します。

TRNSYSでJavaScriptを使ってみる(試作です)

the JavaScript Code / Dmitry Baranovskiy

前回の投稿で、Equationの使い方を紹介しました。Equationはいろいろ使えて便利なんですが、ちょっと複雑な条件になると、それなりに工夫が必要になります。というか、だんだん一見して何の処理か解りにくいのが難点。

どうもプログラマ的な視点でみると、なんで普通にif-then-elseできないんだろうかと、そう思ってしまいます。

まー、でも判定式を実装しようとしたら、それなりに手間だ。ちょっとしたスクリプトを実装するのと変わらない。すげー大変。と、ここまで考えて、ふと気がついた。

オープンソースのスクリプトとか使えるんでは?

今時はフリーで使えるスクリプトエンジンが存在します。ちょっと調べてみると、組み込みできそうなのが幾つかあります。

代表的なのは、

・JavaScript
・Lua
・Ruby

さらに調べてみると、GoogleのJavaScriptエンジンなんかが割と簡単に実装できそうな事がわかります。こことか↓

人の話を聞かない人の V8 エンジン。 V8 を C++ に組み込んで遊ぼう

JavaScriptエンジン、しかもv8って、googleのブラウザ、Chromeに搭載されている超高速なやつです。これはもう、使わない手はないんでないかと。

Google JavaScript V8 Engineをコンポーネントに組み込む

早速、実装してみました。詳細は省きますが、ああだこうだと試行錯誤すること数日、土日を潰して、なんとか動くプロトタイプが出来上がりました。

仕組みは単純で、JavaScriptのエンジンに、コンポーネントのInputs/Outputsの値をやり取りする関数を追加しているだけです。かなりシンプルな拡張です。この関数を使って、JavaScript側でif-then-elseの処理を行います。

試しにEquationの例と同じ、タイムステップが特定の範囲内になったら、出力の値を変更するスクリプトを書いてみました。

例)暖房期以外は冷房期

暖房期 1/1-4/25, 10/21-12/31(1-2760, 7056-)
冷房期  4/26-10/20(暖房期以外)

var time = getInputValue(1);
if(time<=2760 || 7056 <= time)
{
     heating = true;
}
else

     heating = false;
}
cooling = (!heating);
setOutputValue(1, heating);
setOutputValue(2, cooling);

ちなみに前回のEquationで書いたのはこちら。

HEATING = or(le(time,2760 ),ge(time,7056)) 
COOLING = not(HEATING) 

こっちの方が行数が少なくて、なんかスッキリした感じですが、可読性はやっぱりJavaScriptの方がいいですよね?(そんなことない?)

コンポーネントを配置して、スクリプトを用意したら、いよいよSimulation Studioから実行!

おー、ちゃんと動いた。Heatingの状態がOn/Offしているのが確認できます。

If-then-elseが使えりゃいいやって思ってたけど、スクリプトで簡易なコンポーネントのような使い方もできますね。例えば、こんな感じ。

サインカーブを描くスクリプト。

var inp2=getInputValue(1);
var ret;
ret = Math.sin(inp1 / 180 * Math.PI);
setOutputValue(1,ret);

おー、これも無事に動いた。

いろいろ使えそうな感じです。とはいえ、いいことばかりでもなくて実行速度が出ない。これがガッカリするぐらいに遅い。

コンポーネントの仕組み上、イテレーションごとにスクリプトのロードが発生します。おそらく、それが遅くなっている理由。

それとエディタもデバッガーもないので、エラーが起きても原因が何か調べるのも手探り状態。ちょっと普段使いには向かないですね、これじゃ。

スクリプトを使うことを思いついたときは、良いアイディアだと思ったんだけど厳しいですね。

もちょっと試行錯誤が必要そうです。

TRNSYSのFORTRAN開発環境

TRNSYSの開発環境の話

FORTRAN

FORTRAN / goto_

TRNSYSの開発環境といえばインテルFORTRANが標準です。最新版は「インテル® Visual Fortran Composer XE 2013 Windows版」(以下、XE2013)です。
TRNSYSに添付するソリューションは、少し前のXE2011版用ですが、コンパイラの系列としては同じものなので2013でもビルドできます。

このXE2013はパッケージとして2種類販売されています。

インテル Visual Fortran Composer XE 2013 Windows 版 日本語版
インテル Visual Fortran Composer XE 2013 Windows 版 IMSL 同梱

IMSL(IMSL Fortran 数値計算ライブラリー)が同梱されるか、されないかの違いです。このライブラリには数値計算に便利な関数が大量に含まれています。計算によっては、かなり使い勝手の良いライブラリです。
価格が倍以上違いますが、予算的に余裕があれば購入して損は無いです。

が、最新版ではIMSLの配布条件が変更されています。詳細は販売代理店のサイトに記載がありますが、配布先にもライセンスが必要になります。
例えば、IMSLの関数を利用して開発したコンポーネントを配布しようとすると、配布を受ける側にもライセンスが必要になります。

特定の計算用途で開発者自身が使用する場合は問題になりませんが、将来的に配布や販売などを想定している場合は、この配布条件に注意が必要です。無償で公開しようとしたら、IMSLの配布条件できない、なんてことが想定されます。注意して下さい。

FORTRANコンパイラを更新する

FORTRANコンパイラの最新版をゲットしました。

インテル Visual Fortran Composer XE 2013 Windows版

TRNSYSのコンポーネント開発用です。というか、TRNSYSの場合、計算エンジン本体もソースコードが添付してくるので必要に応じてパラメータを変更してビルドし直すことができます。

ということで開発にも運用にも使えます。

いままで使っていたコンパイラよりパフォーマンスも上がりそうなので、ちょっと楽しみです。