1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ごあいさつ

昨今、国内のメタレンズ設計プレイヤーも、市場調査フェーズから実際にツール上で設計するフェーズに移行してきている印象を持っています。
これからメタサーフェース設計される方、今現在設計に着手されている方の後押しになれば幸いです。

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

PlanOpSimとは

PlanOpSimは平面光学およびメタサーフェス設計用ソフトウエア

PlanOpSim APIとは

  • PlanOpSim-APIは外部プラットフォームPythonからPlanOpSimを動作させるAPI
  • PlanOpSim専用SDKが用意されており、Python側からポスト処理することが可能
  • より高度な解析機能を拡張したり、解析操作の自動化で高速化したり..etc

image.png

Pythonインストール

Pythonダウンロードリンクはこちら
https://www.python.org/downloads/windows/
Python 3.10.0以上が必要となります。(2025/12/3現在)

image.png

SDK要求モジュール一覧

コマンドプロンプトを管理者権限で開き以下モジュールインストールコマンドを実行。
例 : py -3.10 -m pip install matplotlib

インフォメーション
Pycharmを使用する場合、このフェーズでのインストールは不要です。

■ 要求モジュール ■

  • pytest>=5.4.2
  • pytest>=Pillow>=9.52
  • numpy~=1.26.4
  • matplotlib~=3.8.2
  • pandas~=2.2.0
  • scipy>=1.4.1
  • seaborn>=0.10.1
  • joblib>=1.1.0
  • pytz>=2022.1
  • numba>=0.53.1
  • Cython>=0.29.30
  • requests~=2.31.0
  • msgpack~=1.0.7
  • beautifulsoup4~=4.12.3
  • lxml>=5.1.0
  • scikit-learn~=1.4.2
  • scikit-optimize~=0.10.1

Pycharmインストール

  • PycharmはPythonに特化したIDE(=Integrated Development Environment)です。​
  • VisualStudioでも良いですが、Pycharmはモジュール管理などが容易です。
  • Ver 2023.1.5は動作実績あり。
  • 光線追跡ツールAnsys Zemax OpticStudio(以降Zemaxと呼称)のZOS-APIでも
    安定性して連携できます。
  • 以下無償版Pycharmダウンロードリンク
    https://www.jetbrains.com/ja-jp/pycharm/download/other.html​

      image.png

Pycharmの紹介

1.  新しいプロジェクトの作成

image.png

2. インタプリタについて
Pythonにおけるモジュール(Module)とは、Pythonコードを整理するためのファイル単位の単位であり、関連する関数、クラス、変数をまとめたものです。モジュールは他のPythonファイルからインポートして利用することができ、コードの再利用性が高まり、プログラムの構造を整理が容易です。​

image.png

3. Pycharmでのモジュールインストール
先程、要求モジュールをスキップしたのは、ここで検索&インストール出来るためです。

image.png

4.日本語への言語変更設定あります。
image.png

PlanOpSim API 立上げ

1.APIサンプルの取得
[Wiki]からサンプルコード、API関数のドキュメントがダウンロード可能です。
image.png

2.フォルダ構成
右のファイルおよびフォルダは同じ階層に保存してください。

  • main.py
  • API_myconfig.py(認証ファイル)
  • APIUtensilClasses(解析用ライブラリフォルダ)

3.API _myconfig.py
-ライセンス認証情報管理に「API _myconfig.py」が必要です。
-APIとPlanOpSimはインタラクティブに接続する際、必ず呼び出す必要があります。

image.png

4.サンプルコードの実行
「新しいプロジェクトの作成」で取得したサンプルをmain.pyにコピーして実行します。
image.png

結像メタレンズ解析自動化の例(コア処理抜粋)

以下の解析の流れを自動化します。

  • メタ原子の構造定義と解析
  • メタ原子解析結果のライブラリ化
  • メタサーフェスの設計と近傍界/遠方界解析

1.メタ原子構造・RCWAスイープ変数の定義

メタ原子の解析条件は以下の通りです。

  • 設計波長:530nm(TE偏光 平面波 垂直入射条件)
  • 400nm x 400nm 周期の円柱ピラー形状のみ
  • ピラー/基板材質:SiO2/Si
  • ピラー高さ:1000nm固定
  • 曲率半径:10nm-190nm(30nmステップ) スイープ変数
############################LOGIN###############################
#login settings have to be set in the API_myconfig.py file
server = API_myconfig.server
POS = PlanOpSim_API(server=server)
POS.login(username=API_myconfig.username, password=API_myconfig.password)

################################################################

runmetacell = True
runmetacomp = True

version = 3
subversion = 2

wavelength = 530
if runmetacell:
   # ###################LOAD  existing file##########################
   # #load and print existing files (not mandatory if you can locate your file based on folder and name directly)
   # configurations = POS.metacell.get_existing_configurations()
   # print(configurations)

   # select your simulation (any combination that gives unique value, if not specified set value to None)
   project = 'FocalLens_v{}'.format(version)
   folderID = None
   simulation = 'FocalLens_v{}_{}'.format(version, subversion)
   simulationID = None
   POS.metacell.load(project=project, projectID=folderID, simulation=simulation,
                     simulationID=simulationID)  # any combination that gives a unique file, id doesn't have to be specified

   # ###############################config###########################
   # edit global parameters: edit the folder name, simulation name, cell width or cell height
   projectname = None
   simulationname = None
   cellWidth = 400
   cellHeight = 400
   POS.metacell.global_parameters.edit(projectname=projectname, simulationname=simulationname, cellWidth=cellWidth,
                                       cellHeight=cellHeight)

   # edit simulation accuracy (diffraction orders of RCWA)
   POS.metacell.simulation_settings.edit_accuracy(accuracy=[4, 4])

   # edit optimizer:
   POS.metacell.simulation_target.edit(name='new_target', optimizer='sweep_validation')


   # create lightsettings
   amplitude = 1
   polarization = 'TE'
   azimuth = 0
   zenith = 0
   POS.metacell.incident_lights[0].edit(amplitude=amplitude, wavelength=wavelength, polarization=polarization,
                                    azimuth=azimuth, zenith=zenith)


   # ###############################create sweepvars###########################
   # #get existing sweep vars and print (just for convenience to check the existing sweep vars, not mandatory, if you know the sweep alias this can be skipped)
   # sweepvars = POS.metacell.get_existing_sweepvars()
   # print(sweepvars) if not sweepvars.empty else print('no existing sweepvars yet')

   # add specific sweepvar based on alias
   POS.metacell.simulation_settings.add_sweep(
       SweepVariable('radius', 'Linear', '10;191;30'))  # note: no special characters allowed

   # create local materials to use (this will use the existing ones from your library)
   Si = Material(name='Si')  # Use Si from library # default None = Use from library;
   SiO2 = Material(name='SiO2', refractive_index=1.5,
                   absorptioncoeff=None)  # use SiO2 from library but overwrite refr index
   Air = Material(name='Air', refractive_index=1, absorptioncoeff=0)  # overwrite both refr index and abs coeff

   # ##############################################define layers##################################
   # edit incident and exit layer, note, these should satisfy rules for incident and output layer
   layer_inc = POS.metacell.structure.layerstack[0]
   layer_inc.edit('Incident layer', thickness='INF', materials=Si)  # none means library value
   layer_out = POS.metacell.structure.layerstack[-1]
   layer_out.edit('Exit layer', thickness='INF', materials=Air)

   # if other layers already exist you can replace
   # them as below with 'POS.metacell.structure.layerstack[n].edit(newlayer)'

   # create additional layers: examples single pillar
   pillar_layer_to_add = CircleLayer(name='pillar', thickness=1000, materials=[Air, SiO2], radius='=radius',
                                     num_steps=[500, 500], center_coord=[0, 0])
   POS.metacell.structure.add_layer(position=1, layer=pillar_layer_to_add)

2. RCWA解析

曲率半径依存の位相および透過率データをプロットしてみます。

#############################################simulate##################################################
    simulationjobID = POS.metacell.simulate(run_if_exist = False)  # uploads/updates simulation and runs it on the server

    #############################################plot##################################################
    try: #check if plot is already existing, disable if you want to update your plots
        PlotInfo_powercoeff = POS.metacell.MCell_plot[0].plotdata
        PlotInfo_fieldcoeff = POS.metacell.MCell_plot[1].plotdata
        assert (PlotInfo_fieldcoeff['yData'] is not None)
    except:
        while len(POS.metacell.MCell_plot) < 2: #initialize 2 empty plots
            POS.metacell.add_plot(
                simulationJob_id=POS.metacell.lastsimulationjob)  # use either response of MCell.simulate or None (last runned simulation)

        inc_light_name = POS.metacell.incident_lights[0].name
        POS.metacell.MCell_plot[0].edit_configurations(plottype='line', xaxes='radius',
                                                       yaxes='{}/T/0,0/TE/power_coeff/Abs'.format(inc_light_name))
        PlotInfo_powercoeff = POS.metacell.MCell_plot[0].run_plot()

        POS.metacell.MCell_plot[1].edit_configurations(plottype='line', xaxes='radius',
                                                       yaxes='{}/T/0,0/TE/field_coeff/Phase'.format(inc_light_name))
        PlotInfo_fieldcoeff = POS.metacell.MCell_plot[1].run_plot()

    ################locally postprocess the plotting data#################################
    # local plot of line plot
    # auto generate plot:
    POS.metacell.MCell_plot[0].show_plot_locally()
    POS.metacell.MCell_plot[1].show_plot_locally()

          透過率                  位相

3. モード解析

Z位置(プローブ)=50nmの位置でのEx(実部) XY断面描画します。

# ###############perform mode analysis#################################
    try:
        mode_analysis = POS.metacell.mode_analysis[0].plot[0]
        assert (mode_analysis['yData'] is not None)
    except:
        while len(POS.metacell.mode_analysis) < 1:
            sweepvarnames = [sweepvar.alias for sweepvar in POS.metacell.simulation_settings.sweepvars]
            sweepvarvalues = [sweepvar.values[0] for sweepvar in POS.metacell.simulation_settings.sweepvars]
            POS.metacell.add_mode_analysis(sweepvarnames, sweepvarvalues, probe = 50)
        POS.metacell.mode_analysis[0].add_plot()
        POS.metacell.mode_analysis[0].plot[0].edit_configurations(quantity='Ex', representation='Re')
        PlotInfo_powercoeff = POS.metacell.mode_analysis[0].plot[0].run_plot()
    POS.metacell.mode_analysis[0].plot[0].show_plot_locally()

          

スイープ変数の範囲が大きい場合、ChromeやMicrosoft Edge等のブラウザでは、
メモリ不足で描画処理動作が不安定となりがちです。(Firefoxは比較的安定してます。)

→APIの場合は、一度の処理でデータを授受出来るため、処理動作を気にすることないこともメリットの一つですね。

4. φ100μm メタサーフェス 設計ターゲット定義

※先程のメタ原子RCWA Sim結果を全てライブラリ化しています。

  • TE偏光平面波のみ 垂直入射 530nm
  • メタ原子配列のターゲット位相を (2π/λ) * (√(x² + y² + f²) - f)の球面式で近傍界設計
  • 焦点距離 500μm
################################################################################################
    # ######################################make new library##########################################
    # #get all existing families#just for reference if you need to find the id of an existing group
    # mcell_groups = POS.library.get_mc_groups()
    # print(mcell_groups)

    # start from new group
    # ##### load existing group from server
    name = simulation + '_family_example'
    id = None
    metacell_group = POS.library.load_mcell_group(name=name,
                                                  id=id)  # if name is unique, the id doesn't have to be given, if grout doesn't exist a new one will be created

    # select all members from metacell simulation
    sweepvar_list = POS.metacell.get_sweepvar_list()
    sweepvarnamelist = [sweepvar.alias for sweepvar in sweepvar_list]
    valuelists = [np.array(combination) for combination in
                  itertools.product(*[sweepvar.values for sweepvar in sweepvar_list])]
    for valuelist in valuelists:
        metacell_group.add_member(POS.metacell.lastsimulationjob, sweepvarnamelist, valuelist)

    POS.library.update_mcell_group(metacell_group)  # create new group

if runmetacomp:

    ################################################################################################
    # ######################################make new component########################################

    # #load and print existing metacomponent files (not mandatory if you can locate your file based on folder and name directly)
    # configurations = POS.metacomponent.get_existing_configurations()
    # print(configurations)

    # select your simulation (any combination that gives unique value, if not specified set value to None)
    comp_group = 'FocalLens_v{}'.format(version)
    comp_group_id = None
    component = 'FocalLens_v{}_{}'.format(version, subversion)
    component_id = None
    POS.metacomponent.load(comp_group=comp_group, group_id=comp_group_id, meta_component=component,
                           meta_component_id=component_id)  # any combination that gives a unique file, id doesn't have to be specified

    # ###############################config###########################
    # edit global parameters: edit the folder name, simulation name, cell width or cell height
    component_group = None
    metacomponent_name = None
    component_width = 100
    component_length = 100
    unit = 'mum'
    POS.metacomponent.component_dimensions.edit(component_group=component_group, metacomponent_name=metacomponent_name,
                                                component_width=component_width, component_length=component_length,
                                                unit=unit)

    # add metacell group, and metacells (member list or None for all)
    name = 'FocalLens_v{}_{}'.format(version, subversion) + '_family_example'
    id = None
    metacell_group = POS.library.load_mcell_group(name=name, id=id)
    # if name is unique, the id doesn't have to be given, if group doesn't exist a new one will be created

    POS.metacomponent.add_metacell_group(group=metacell_group, member_list=None)

    if len(POS.metacomponent.targets) == 0:
        POS.metacomponent.add_target()
    POS.metacomponent.targets[0].setpoint.edit(name='sp_target1', amplitude=1, wavelength=530, polarization='TE', azimuth=0,
                                               zenith=0, unit='nm',
                                               type='GB', focalspot=[0, 0, 0], beam_divergence=0.05)  # note: unit only for setpoint wavelengt as in the GUI!
    POS.metacomponent.targets[0].edit(name='target 1', description = 'SDK created', optimize_for='Complex', weight=1)
    POS.metacomponent.targets[0].metacell_accuracy.edit(acc_x=4, acc_y=4)
    POS.metacomponent.targets[0].output_light.edit(order_x=0, order_y=0, direction='Transmission', polarization='TE')

    # example code to add 2nd target
    # if len(POS.metacomponent.targets) == 1:
    #     POS.metacomponent.add_target()
    #
    # POS.metacomponent.targets[1].setpoint.edit(name='sp_target2', amplitude=1, wavelength=540, polarization='TE', azimuth=0,
    #                                             zenith=0, focalspot=[10, 20, 30], type='GB', beam_divergence=5,
    #                                             unit='nm')  # note: unit only for setpoint wavelengt as in the GUI!
    #
    # POS.metacomponent.targets[1].edit(name='target 2', description='SDK created', optimize_for='Complex', weight=1)
    # POS.metacomponent.targets[1].metacell_accuracy.edit(acc_x=4, acc_y=4)
    # POS.metacomponent.targets[1].output_light.edit(order_x=0, order_y=0, direction='Transmission', polarization='TE')



    # example to add static script from txt file
    # POS.metacomponent.targets[-1].nfwf.filename='NFWF_script.txt'

    # example to make a dynamic script string

    def nfwf_lens_script(wl, f):
        nfwfscript = "wavelength = {}; \
        f = {};\
        phase = 2 * np.pi / wavelength * (np.sqrt(x ** 2 + y ** 2 + f ** 2) - f);\
        amplitude = np.ones_like(x);\
        phase = np.mod(phase, 2 * np.pi)".format(wl, f)
        return nfwfscript


    focaldistance = 500
    POS.metacomponent.targets[0].nfwf.script_string = nfwf_lens_script(wl=wavelength / 1000,
                                                                        f=focaldistance)  # note comp dimension is in unit=mum!

    #example code for second target
    # focaldistance = 2000
    # POS.metacomponent.targets[1].nfwf.script_string = nfwf_lens_script(wl=wavelength / 1000,
    #                                                                     f=focaldistance)  # note comp dimension is in unit=mum!

ターゲットとする振幅と位相分布断面です。
今回球面式で与えていますが、Zemaxから位相データをインポートもできます。
image.png

5. メタ原子配列

PlanOpSim側で自動配列でき、GDS上で円柱ピラーが並んでいることを確認できました。

############################################simulate##################################################
    designJob_id = POS.metacomponent.design()
    GDS_file = POS.metacomponent.create_gds(foldername='gds_download')

        

5. メタレンズの近傍界解析と遠方界解析、フーリエ解析

  • 遠方界解析では、設計波長530nmの集光スポット Z=500μm±250μmのXY分布を解析
  • 近傍界では、メタレンズ透過直後の位相振幅分布を解析
########################################analysis##################################################
    try:
        Analysis_info_nfwf = POS.metacomponent.analysis[0].plotdata
    except:
        while len(POS.metacomponent.analysis) <= 0:
            POS.metacomponent.add_analysis(
                designJob_id=POS.metacomponent.lastdesignjob)  # use either response of MCell.simulate or None (last runned simulation)

    POS.metacomponent.analysis[0].add_analysis_sweep_var('z', focaldistance / 2, focaldistance * 2 + 1E-5,
                                                         focaldistance / 2)
    POS.metacomponent.analysis[0].edit_ff_properties(width=100, rows_count=100, height=100, columns_count=100,
                                                     unit='μm', center_coord=[0, 0, '=z'])
    POS.metacomponent.analysis[0].setpoint.edit(wavelength=500,unit='nm', diffractive_order = [3,3])
    POS.metacomponent.analysis[0].type = 'ALL'
    POS.metacomponent.analysis[0].run()
    POS.metacomponent.analysis[0].show_analysis_locally()

遠方界解析結果

image.png

近傍界解析結果

image.png

勿論ですが、手動解析と同じ解析結果が自動で出力できました。
メタ原子解析からメタレンズの伝搬解析まで約5分程度で実行できました。
(実オペレーションだと体感30分程度でしょうか..)

最終的には、Zemax上の光学系データと連成解析し、メタレンズ設計にフィードバックまで自動化できるとより簡便ですよね。

お問い合わせ

  • より詳細なAPI関数を学習するには、サンプル内のドキュメントをご覧頂くか、
     弊社サポートサイトをご活用下さい。
  • 光学シミュレーションソフトの導入や技術相談、
    設計解析委託をお考えの方はサイバネットにお問合せください。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?