1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[python×Ansys Lumerical]周期的に極性反転した光導波路(疑似位相整合の光導波路)を自動作成する方法

Last updated at Posted at 2025-12-16

問い合わせフォームのご連絡

この記事の内容についてのご質問や、
Ansys(Lumerical / HFSS など)の使い方相談はもちろん、
フリーソフト・OSSを使ったシミュレーションや解析の技術的なご相談も受け付けています。
「この設定で合ってる?」「この解析、どう進めるのがよい?」
「商用ソフトとフリーソフト、どう使い分けるべき?」
といったレベル感でも問題ありません。
気軽にこちらの問い合わせフォームからご連絡ください👇
https://form.cybernet.co.jp/optical/contact/qiita_form/

はじめに

LiNbO₃ に代表される 周期分極反転(Quasi-Phase Matching, QPM)構造は、
SHG などの非線形光学デバイスでは定番です。

それをFDTDシミュレーションしようとすると
ポーリング周期が数十 µmで何十〜何百周期も必要となるため
GUIで作るのは正直つらく

手作業での形状作成が破綻しがちです。

そこで今回は、
Python × Ansys Lumerical FDTDで周期的に分極反転した形状を自動で作成する方法をご紹介します。

今回のワークフローは以下です。
①分極が↑方向の材料と、↓方向の材料を手動で設定しておき、
プロジェクトファイル(.fsp)ファイルとして保存

②そのプロジェクトファイルに構造を追加して周期的に分極反転した形状を作成

という手順を行います。材料登録も自動でできますが、そこはGUI上で実施した方がミスが起きにくいので、そうしました。そんなに手間ではないですし。

想定する構成・前提

対象デバイス

LiNbO₃ 導波路
周期分極反転(QPM)
SHG を想定(χ²材料)

座標系の定義

光の伝搬方向:+x
QPM周期方向:+x(伝搬方向と同じ)
導波路断面:y–z 面

材料について

材料は GUIで手動登録する

Python 側では「材料名を使うだけ」

GUIで事前に作っておく材料
Material Explorer で以下の材料を登録しておき、
base_with_materials.fspとして保存しておきます。
スクリーンショット 2025-12-15 134146.png

材料名 内容
MAT_LN_CORE_LINEAR LiNbO₃(線形屈折率のみ)
MAT_LN_QPM_CHI2_POS Chi2材料(χ² > 0)
MAT_LN_QPM_CHI2_NEG Chi2材料(χ² < 0)

MAT_LN_QPM_CHI2_POS / NEG は
Base material に MAT_LN_CORE_LINEAR を指定

Diagonal chi2(zzz 成分のみ)を使用

実装方針
周期分極反転 = χ² の符号反転

構造的には
+χ² ドメイン
−χ² ドメイン
を x方向に交互に並べる

実際のスクリプト

形状定義用のpythonスクリプト(shape_builder.py)

from dataclasses import dataclass
from datetime import datetime


@dataclass(frozen=True)
class MaterialNames:
    CORE_LINEAR_MAT: str = "MAT_LN_CORE_LINEAR"
    QPM_POS_MAT: str = "MAT_LN_QPM_CHI2_POS"
    QPM_NEG_MAT: str = "MAT_LN_QPM_CHI2_NEG"
    CLAD_MAT: str = "MAT_CLAD_SIO2"  # 任意


class QPMWaveguideBuilder:
    """
    QPMドメインを +x 方向に並べて導波路コアを作る(伝搬方向 +x、周期方向 +x)。
    eval() を使わず、既存プロジェクトに「追記」する前提の安全版。
    """

    def __init__(self, group_name_prefix: str = "GEO_QPM_WG"):
        self.group_name_prefix = group_name_prefix
        self.mat = MaterialNames()

    def expected_material_names(self) -> MaterialNames:
        return self.mat

    def _make_unique_group_name(self) -> str:
        ts = datetime.now().strftime("%Y%m%d_%H%M%S")
        return f"{self.group_name_prefix}_{ts}"

    def build_qpm_core(self,
                       fdtd,
                       guide_width_y: float,
                       guide_thickness_z: float,
                       period: float,
                       duty: float,
                       num_periods: int,
                       center_y: float = 0.0,
                       center_z: float = 0.0,
                       start_x: float = 0.0,
                       group_name: str | None = None) -> str:
        """
        guide_width_y      : y span [m](導波路幅)
        guide_thickness_z  : z span [m](導波路厚さ)
        period             : Λ [m](x方向の周期 = 伝搬方向)
        duty               : 0-1(positive domain length fraction)
        num_periods        : 周期数
        start_x            : 最初のドメイン開始位置(x)
        center_y, center_z : 断面中心

        生成されるドメインは x 方向に並ぶ(伝搬方向に沿った分極反転)。
        """

        if not (0.0 < duty < 1.0):
            raise ValueError("duty must be between 0 and 1 (exclusive).")

        num_periods = int(num_periods)
        Lp = period * duty
        Ln = period * (1.0 - duty)

        # --- 新しいグループを作る(必ずユニーク名) ---
        if group_name is None:
            group_name = self._make_unique_group_name()

        fdtd.addstructuregroup()
        fdtd.setnamed("structure group", "name", group_name)

        # グループ位置(任意)
        fdtd.setnamed(group_name, "x", start_x)
        fdtd.setnamed(group_name, "y", center_y)
        fdtd.setnamed(group_name, "z", center_z)

        # --- 周期ドメインを生成(x方向に配列) ---
        for i in range(num_periods):
            idx = i + 1
            x0 = start_x + i * period

            # + domain(長さ Lp を x 方向に持つ)
            name_pos = f"dom_pos_{idx}"
            fdtd.addrect(name=name_pos)
            fdtd.setnamed(name_pos, "material", self.mat.QPM_POS_MAT)

            fdtd.setnamed(name_pos, "y", center_y)
            fdtd.setnamed(name_pos, "y span", guide_width_y)

            fdtd.setnamed(name_pos, "z", center_z)
            fdtd.setnamed(name_pos, "z span", guide_thickness_z)

            fdtd.setnamed(name_pos, "x", x0 + 0.5 * Lp)
            fdtd.setnamed(name_pos, "x span", Lp)

            fdtd.addtogroup(group_name)

            # - domain(長さ Ln を x 方向に持つ)
            name_neg = f"dom_neg_{idx}"
            fdtd.addrect(name=name_neg)
            fdtd.setnamed(name_neg, "material", self.mat.QPM_NEG_MAT)

            fdtd.setnamed(name_neg, "y", center_y)
            fdtd.setnamed(name_neg, "y span", guide_width_y)

            fdtd.setnamed(name_neg, "z", center_z)
            fdtd.setnamed(name_neg, "z span", guide_thickness_z)

            fdtd.setnamed(name_neg, "x", x0 + Lp + 0.5 * Ln)
            fdtd.setnamed(name_neg, "x span", Ln)

            fdtd.addtogroup(group_name)

        return group_name

形状追加用のmainスクリプト(main.py)

"""
main.py

既存の .fsp(材料登録済み)を開き、
QPM(周期分極反転)導波路コア形状(伝搬方向 +x、周期方向 +x)を追加して保存する。
"""

import os
import lumapi
from shape_builder import QPMWaveguideBuilder


# ====== ここだけ自分の環境に合わせて設定 ======
BASE_FSP = r"C:\Users\babatake\Documents\QuasiPhasematching1\base_with_materials.fsp" #ここは適宜変更してください
OUT_FSP  = r"C:\Users\babatake\Documents\QuasiPhasematching1\base_with_materials_plus_qpm_geo.fsp"


def main():
    if not os.path.isfile(BASE_FSP):
        raise FileNotFoundError(f"Base .fsp not found: {BASE_FSP}")

    fdtd = lumapi.FDTD()

    # 1) 材料登録済みプロジェクトをロード
    fdtd.load(BASE_FSP)

    # 2) パラメータ(伝搬方向 +x を想定)
    guide_width_y     = 2.0e-6   # y span [m](導波路幅)
    guide_thickness_z = 0.8e-6   # z span [m](厚さ方向)
    period            = 16e-6    # QPM period Λ [m](x方向=伝搬方向)
    duty              = 0.50
    n_periods         = 40

    # 配置(伝搬方向 x に沿って並べる)
    start_x  = 0.0     # x方向の開始位置(QPMの並び開始)
    center_y = 0.0     # 断面中心 y
    center_z = 0.0     # 断面中心 z

    # 3) 形状追加
    builder = QPMWaveguideBuilder(group_name_prefix="GEO_LN_QPM_WG")

    mats = builder.expected_material_names()
    print("Expected materials in the loaded project:")
    print("  QPM_POS_MAT =", mats.QPM_POS_MAT)
    print("  QPM_NEG_MAT =", mats.QPM_NEG_MAT)

    grp_name = builder.build_qpm_core(
        fdtd,
        guide_width_y=guide_width_y,
        guide_thickness_z=guide_thickness_z,
        period=period,
        duty=duty,
        num_periods=n_periods,
        start_x=start_x,
        center_y=center_y,
        center_z=center_z
    )
    print("Created group:", grp_name)

    # 4) 保存(上書きしたいなら fdtd.save(BASE_FSP) でもOK)
    fdtd.save(OUT_FSP)
    print(f"Saved: {OUT_FSP}")


if __name__ == "__main__":
    main()

事前に材料を登録して保存しておいたbase_with_materials.fspをmainファイルから呼び出して、周期構造を追加しています。

実行結果

スクリーンショット 2025-12-15 134034.png

こちらが実行結果になります。
分極の向きが+z方向のdom_posと分極の向きが-z方向のdom_negマテリアルが交互に連なっていることが確認できます。

この後はFDTDシミュレーション条件を追加すればSHGのシミュレーションができるようになります。

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

問い合わせ

光学シミュレーションソフトの導入や技術相談、
設計解析委託をお考えの方はサイバネットシステムにお問合せください。

光学ソリューションサイトについては以下の公式サイトを参照:
👉 光学ソリューションサイト(サイバネット)

光学分野のエンジニアリングサービスについては以下の公式サイトを参照:
👉 光学エンジニアリングサービス(サイバネット)

1
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?