光線追跡を実行する際に、計算に使用する固定データを OpticStudio に読み込む必要が生じる場合があります。この記事では、テキスト ファイルからデータを取り込み、それを使用してユーザー定義光源を作成する方法を解説します。この手法は、任意の面とデータ型に応用できます。
著者 Sanjay Gangadhara
ダウンロード
序論
光線追跡を実行する際に、計算に使用する固定データを OpticStudio に読み込む必要が生じる場合があります。その一例として、データを使用した任意のアポダイゼーション関数の定義があります。使用するデータが、OpticStudio に用意された既存のどのモデルにも適合しない場合に、ここで紹介する方法が効果的です。ユーザー定義面を介して、そのようなデータを OpticStudio に読み込むことができます。ユーザー定義面の作成方法に関する詳細は、ナレッジベースの記事「ユーザー定義 DLL をコンパイルする方法」を参照してください。
ファイルからユーザー定義面への静的データの読み込み
この記事には、ユーザー定義面を使用してアポダイゼーション関数を定義する方法を示すサンプル ファイル (Reading_filter.ZMX) が添付されています。この設計では 2 枚のレンズを使用しています。1 枚は軸上の視野点からの光を平行光線にして、もう 1 枚はそのビームが像面上の 1 点に合焦するようにします。これらのレンズの間には、透過フィルタとして機能する中間面が配置されています。この光学系は、マルチ コンフィグレーションでモデル化されています。コンフィグレーション 1 では、有限の透過率を設定した中間面にアポダイゼーション関数を適用していますが、コンフィグレーション 2 では適用していません (中間面の透過率を 1 としています)。コンフィグレーション 1 で使用するアポダイゼーション関数を、ファイル "Transmission.txt" から読み込みます。ユーザー定義面を使用して、この関数を中間面 (この例では面 3) に適用します。この面のソース コードと DLL (それぞれ "US_FILT_FILE.cpp" と "US_FILT_FILE.DLL") は、この記事に添付されています。添付の ZIP ファイルには透過率データのファイルも用意されています。このファイルを {Zemax}\Miscellaneous ディレクトリに保存する必要があります。
この DLL は、US_FILT 面の定義に使用している DLL によく似ています (ヘルプ システム ファイルの「[設定] (Setup) タブ > [エディタ] (Editor) グループ ([設定] (Setup) タブ) > [レンズ データ エディタ] (Lens Data Editor) > シーケンシャル面 (レンズ データ エディタ) > ユーザー定義 」を参照)。
ただし、この DLL が正規化円形座標 (r) に対して指数関数的に変化するアポダイゼーション関数を指定しているのに対し、US_FILT_FILE.DLL では、さまざまな r における透過率の値をユーザー側で指定できるようになっています。このデータ ファイルでは、次の図に示すように、先頭行でデータ要素の数を指定し、それに続く各行で r とその位置での透過率を記述する必要があります。
この DLL では、このファイルにない任意の r における透過率の値が、ファイルで入力したデータを線形補間して計算されます。
OpticStudio の他の DLL と同様に、この DLL も光線を追跡するたびに呼び出されます (レイアウト図の作成や解析機能の実行など)。最大限の計算効率を得るために、OpticStudio による光線追跡の計算はマルチスレッド化されています。したがって、計算中は、この DLL のコピーが同時に多数開いています。
DLL のコピーが作成されるたびに (新しい解析機能を開くときや現在の機能を更新するときなど) OpticStudio でデータを読み取る必要があるとすると、計算に長時間を要するように思われます。
しかし幸いにも、そのような状態にはなりません。DLL を最初に OpticStudio で読み取るときに (面タイプを ユーザー定義タイプに設定して、面の DLL を USER_FILT_FILE.DLL に設定している場合など)、下記の DLL_PROCESS_ATTACH のステップでデータが一度だけ読み取られます。
BOOL WINAPI DllMain (HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
FILE *in;
struct parse_type *pars;
switch(ul_reason_for_call)
{
/* Read in the data when the DLL first loads */
case DLL_PROCESS_ATTACH:
/* Allocate memory for the transmission data */
xtrans = (double*)malloc(MAX_PATH_LENGTH * (MAX_TEXT_PARS+1));
trans = (double*)malloc(MAX_PATH_LENGTH * (MAX_TEXT_PARS+1));
pars = (parse_type*)malloc(sizeof(struct parse_type) * (MAX_TEXT_PARS+1));
/* Open the text file containing the
transmission data */
in = NULL;
if ((in = fopen("C:\\Program Files\\Zemax\\DLL\\Transmission.txt", "rt")) == NULL) return(0);
/* Read in the number of data points */
fgets(disp, MAX_PATH_LENGTH, in);
n_trans = atoi(disp);
/* Read in the transmission data */
for (i=1; i<=n_trans; i++)
{
fgets(disp, MAX_PATH_LENGTH, in);
Parser(disp, pars, MAX_TEXT_PARS);
xtrans[i] = strtod(pars[1].n,NULL);
trans[i] = strtod(pars[2].n,NULL);
}
/* Close the file, and free the memory associated
with the text parser */
if (in) fclose(in);
in = NULL;
if (pars) free(pars);
pars = NULL;
break;
case DLL_PROCESS_DETACH:
/* Free the memory associated with the transmission
data once the DLL is unloaded */
if (xtrans) free(xtrans);
xtrans = NULL;
if (trans) free(trans);
trans = NULL;
break;
}
return TRUE;
}
このステップは、OpticStudio に DLL を最初に読み取るときにのみ実行され、座標データと透過率データを保持する変数 (この例では、それぞれ XTRANS と TRANS)、およびファイルからのデータ読み取りに使用する変数 (PARS) にメモリを割り当てます。つづいて目的のファイルを開き、データを読み取ります。すべてのデータを読み取り、該当の変数に格納するとファイルを閉じ、ファイル読み取り変数に割り当てたメモリを解放します。XTRANS と TRANS をグローバル変数として定義することで (プログラムの冒頭、DllMain を呼び出す前の初期化処理)、以降のすべての光線追跡でこれらの読み取ったデータを使用できるようになります。
/* define the global variables, which will be available
during all calls to the DLL */
struct parse_type
{
char n[MAX_PARSE_LENGTH];
};
int n_trans, i;
char disp[MAX_PATH_LENGTH];
double *xtrans, *trans;
DLL 自身の switch(FD->type) で case 8 フラグを使用することで、この DLL にデータを読み取ることもできます。しかし、DLL の初期化フラグに相当する case 8 は、DLL を呼び出すたびに呼び出されます。新しい解析機能を開くときや現在の機能を更新するときなどが該当します。したがって、この方法では、データを何回も読み取る必要があります。DLL_PROCESS_ATTACH のステップで 1 回だけデータを読み取る方が、はるかに効率的です。データの読み取りが 1 回のみであることから、外部データを使用しても光線追跡に長時間を要することがありません。この DLL 自身でデータを読み取る方法を説明したサンプルも添付ファイルに用意されています ("US_FILT_FILE2.cpp" と "US_FILT_FILE2.DLL")。DLL は複数のスレッドで呼び出されるので、排他制御を使用する必要がある点に注意してください。このソース コードのもう一つの変更点は、フィルタのファイル名を LDE のコメントとして指定する機能です。
データがメモリからアンロードされるのは、DLL を OpticStudio からアンロードした場合のみです (プログラムを終了したときや面タイプを変更したときなど)。この時点で DLL_PROCESS_DETACH のステップが実行され、グローバル変数に割り当てられていたメモリが解放されます。
これに続いて DLL を OpticStudio で再度読み取る場合、ファイルのデータも読み取る必要がありますが、この場合もそれは1 回のみです。したがって、ファイルのデータを変更したために、OpticStudio で読み取り直すことが必要になった場合でも、面タイプを変更することで DLL をいったんアンロードし、再度ロードすれば新しいデータが読み取られます。今回の例では、外部データを OpticStudio に読み取る便利な方法を紹介しました。この手法は、任意の面とデータ型に応用できます。
KA-01488
コメント
記事コメントは受け付けていません。