1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Zemax → CODEVへの変換で材料名エラーに困ったら

Last updated at Posted at 2025-09-30

お客様からよくある声

光学設計ソフトウェア「Ansys Zemax OpticStudio」(以下、Zemax)には
「CODEV Converter」というデータ変換機能が標準搭載されており、
レンズデータをCODEV(※)形式に変換できます。
(※光学設計ソフトウェア「CODEV」を指します(以下、CODEV))

しかし実際に使ってみると、

  • 材料名(ガラス名)がZemaxとCODE Vで微妙に異なる
  • その結果、変換途中でエラーが出て止まってしまう

といったトラブルに悩まれるお客様が多いのが実情です。

そこで今回、ZemaxとCODEVの材料名を事前に突合し、
不一致をリストアップするPythonスクリプトを作ってみました。
このスクリプトを使えば、変換前にどの材料名が
「完全一致/近似一致/どちらかにしか存在しないか」を把握でき、
後工程での自動置換や変換精度向上に役立ちます。

最終的にはここで得られた対応関係を活用し、
Zemaxデータを自動修正 → CODEV Converterを起動 → エラーなく変換完了
までの一連処理を自動化することを目標としています。

この記事でやること

  • Zemaxの.agf(ガラスカタログ)と、CODEVの.xml(ガラス名リスト)を読み込み
  • 「完全一致/近似一致/Zemaxのみ/CODEVのみ」へ分類
  • 一覧をCSVで出力(後工程での自動置換に使える)

対象ファイル形式のポイント

  • Zemax :行頭がNMの行に材料IDが出ます(例:NM N-BK7)。
  • CODEV :<GlassName>...</GlassName>//タグ内にガラス名が入ります。

近似一致の考え方(表記ゆれの吸収の仕方)

コンバーター実行の前に以下のようなゆるい正規化で突合精度を上げます:

  • 大文字統一・ハイフン除去(N-BK7 → NBK7)
  • 先頭E-/J-接頭辞の無視
  • 先頭アルファベット+ゼロ詰め番号のゼロ落とし(LAH50050 → LAH550のような型番整形)

実務上ありがちな表記差を吸収し、
「ほぼ同じだが記法が違う」というものを近似一致として拾います。

コア処理(抜粋)

ここからが実際の処理方法の紹介です。

1. .agfから材料IDを抽出

import re

def extract_nm_from_agf(file_path):
    nm_ids = set()
    with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
        for line in f:
            if line.lstrip().startswith("NM"):
                parts = re.split(r'[,\s]+', line.strip())
                if len(parts) >= 2:
                    nm_ids.add(parts[1])  # 例: "N-BK7"
    return sorted(nm_ids)

2. CODEV .xmlからを抽出

import xml.etree.ElementTree as ET

def extract_glassnames_from_xml(file_path):
    names = set()
    tree = ET.parse(file_path)
    root = tree.getroot()
    for elem in root.findall('.//GlassName'):
        if elem.text:
            names.add(elem.text.strip())
    return sorted(names)

3. 簡易正規化(近似一致用)

import re

def normalize_name(name: str) -> str:
    name = name.upper().replace("-", "")
    name = re.sub(r"^[EJ]-?", "", name)              # E- / J- 接頭辞の無視
    name = re.sub(r"([A-Z]+)0+(\d+)", r"\1\2", name) # ゼロ詰め番号のゼロ落とし
    return name

4. 2つのリストの比較 → 4分類

def compare_two_lists(zemax_list, codev_list):
    zemax_set, codev_set = set(zemax_list), set(codev_list)
    exact = sorted(zemax_set & codev_set)

    # 近似一致のための正規化マップ
    zemax_only = sorted(zemax_set - codev_set)
    codev_only = sorted(codev_set - zemax_set)
    normalized_z = {normalize_name(n): n for n in zemax_only}
    normalized_c = {normalize_name(n): n for n in codev_only}

    partial_matches, remaining_z, remaining_c = [], [], []
    for nz, oz in normalized_z.items():
        if nz in normalized_c:
            partial_matches.append((oz, normalized_c[nz]))
        else:
            remaining_z.append(oz)
    for nc, oc in normalized_c.items():
        if nc not in normalized_z:
            remaining_c.append(oc)

    return exact, partial_matches, remaining_z, remaining_c

5. 一括比較とCSV出力

import os
import pandas as pd

def compare_all_by_filename(zemax_folder, codev_folder, output_csv):
    zemax_files = {os.path.splitext(f)[0]: os.path.join(zemax_folder, f)
                   for f in os.listdir(zemax_folder) if f.lower().endswith(".agf")}
    codev_files = {os.path.splitext(f)[0]: os.path.join(codev_folder, f)
                   for f in os.listdir(codev_folder) if f.lower().endswith(".xml")}

    all_keys = sorted(set(zemax_files.keys()) | set(codev_files.keys()))
    records = []

    for name in all_keys:
        zemax_list = extract_nm_from_agf(zemax_files.get(name)) if name in zemax_files else []
        codev_list = extract_glassnames_from_xml(codev_files.get(name)) if name in codev_files else []

        exact, partial, z_only, c_only = compare_two_lists(zemax_list, codev_list)

        for v in exact:
            records.append({"ファイル名": name, "一致種別": "完全一致", "Zemax": v, "CODEV": v})
        for z, c in partial:
            records.append({"ファイル名": name, "一致種別": "近似一致", "Zemax": z, "CODEV": c})
        for z in z_only:
            records.append({"ファイル名": name, "一致種別": "Zemaxのみ", "Zemax": z, "CODEV": ""})
        for c in c_only:
            records.append({"ファイル名": name, "一致種別": "CODEVのみ", "Zemax": "", "CODEV": c})

    pd.DataFrame(records).to_csv(output_csv, index=False, encoding="utf-8-sig")
    print(f"✔ 比較結果を {output_csv} に出力しました")

実行結果例

下記のような構成のcsvファイルが生成されます。

ファイル名 一致種別 Zemax CODEV
HIKARI 近似一致 J-SK5 JSK5
HIKARI 近似一致 SSK1 JSSK1
HOYA 完全一致 FDS1 FDS1
ZEON Zemaxのみ ZEONEX_T62R_2017
ZEON CODEVのみ T62R
  • 完全一致:そのまま通る可能性が高い
  • 近似一致:自動置換候補(マッピング表へ)
  • 片側のみ:供給カタログ差 or 命名差の要調査

よくある表記差(例)

今回試してみた結果、CODEVの方は材料名の文字数制限がかなり厳しいみたいなので
正式名称でなく省略した名前が採用されることが多いようです。

  • N-BK7 ↔ NBK7(ハイフンの有無)
  • E-FK5 ↔ FK5(接頭辞E-の有無)
  • LAH50-000 ↔ LAH50(ゼロ詰め・サフィックス差)

次の一歩:完全自動化までのロードマップ

今回は材料名の比較のみでしたが、最終的には以下の流れで
変換の実効までを完全自動化したいですね。

  1. マッピング表作成
    近似一致や手動確認の結果から、Zemax名 → CODE V名の対応CSVを作成。

  2. Zemaxモデルの材料名一括置換(ZOS-API)
    ・ レンズファイル(.zmx)読み込み
    ・材料名をマッピングに従い置換し、強制書き換え

  3. 純正「CODE V Converter.exe」を自動起動
    ・ZOS-API もしくはsubprocess等で実行
    ・変換ログを取得し、未解決材料をレポート

  4. 回帰テスト
    サンプル設計群に対して一括変換+差分検証で壊れていないかを担保

ここまでをワンボタン化できると日々の変換運用が非常に楽に!

まとめ

  • 変換失敗の主要因である材料名不一致を事前に見える化。
  • 完全一致/近似一致/片側のみに分類し、置換の優先順位を決められる。
  • 今後はこの結果を使って自動置換→純正コンバーター起動までを繋ぎ込み予定。

問い合わせ

本記事は前処理スクリプトの要点のみの紹介でした。
スクリプト全文作成/導入支援/カスタマイズをご希望の方は、
サイバネットシステム光学エンジニアリングサービスまでお問い合わせください。

本件とは関係なく、光学設計・解析・光学教育などのご依頼もぜひ!




※本記事は筆者個人の見解であり、所属組織の公式見解を示すものではありません。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?