python光學仿真面向對象光學元件類的實現

光學元件類

平面反射鏡是一種極為簡單的模型,因為我們隻需要考慮一個平面即可。但是除此之外的其他光學元件,可能會變得有些復雜:我們必須考慮光在入射面和出射面的行為。

這當然是一句廢話,而且我們也有瞭一個初步的解決方案:將光學元件拆成前表面和後表面即可。如果光需要在光學元件中反射多次,那就將光學元件拆成需要反射次數的表面個數即可,完美而無腦。

這說明我們已經熟悉瞭程序員的思維,我們眼中的世界已經不再是一個所見即所得的世界,我們看到的是一個個抽象零部件的表現。但是也不要驚慌,程序員和正常人也未必有很大的區別,因為我們除瞭可以將這個世界拆解,也可以將拆解之後的部件重新構造回這個世界。

嘗試著將問題想得復雜一些,光學系統中有許多光學元件,光會透過每個光學元件很多次,而且每次的入射點、出射點都會有一定的偏差。由於光學元件可能會對光的能量有所吸收,從而引起發熱。而且每次的入射點、出射點不同,則發熱位置也不一樣。由於發熱會導致光學元件發生形變,所以下一次光和光學元件的作用也會發生變化。

也就是說,對於每個光學元件來說,除瞭有固定的前表面、後表面,還有入射點、出射點、發熱、形變等不斷變化的參數。這樣的一個過於實際的問題促使我們構造一種更加貼近現實的數據類型,換句話說,我們要創建一個對象,這個對象能夠封裝各種變量和功能,我們輸入一個參量,這個對象的狀態也會跟著發生變化。

這就是所謂的面向對象。

class Opti():
    def __init__(self,edge1,edge2):
        self.edge1 = edge1
        self.edge2 = edge2

在上例中,我們定義瞭一個光學元件類,這個光學元件有兩個表面,這兩個表面既可以是平面,也可以說弧面。這樣,我們就建立瞭一個類。其中,__init__為初始化方法,self表示我們所創建的這個類本身。一般來說,如果類中的方法不加修飾符的話,就必須將self當作第一個參數。

self.edge1表示這個Opti類中,有一個成員的名字叫edge1。當這個類被初始化的時候,我們就可以對其進行賦值瞭。

有些元件可能隻有一個表面,比如全反鏡;有些可能有多個表面,比如偏振立方體。而且,我們在做實驗的時候,也需要對不同的光學元件進行比較,從而得到最好的實驗結果。所以,如果我們想改變已經建好的光學元件,應該怎麼辦呢?

其實很簡單,隻要增加一個方法,使得可以插入或者刪除新的表面即可。

#文件Opti.py
class Opti():
    def __init__(self,edges=[[(0,-1),(0,1)],[(0,1),(0,-1),(1/2,0)]]):
        self.edges = [{'index':i,'dots':edges[i]}
                      for i in range(len(edges))]
    #edge格式為(dot1,dot2,...)
    def insertEdge(self,edge,albedo=0):
        self.edges.append(
            {'index':len(self.edges),'dots':edge})
    #可接受編號和點集
    def delEdge(self,edge):
        try:
            if isinstance(edge,list):   #如果edge的類型是list
                for edg in self.edges:
                    if edg['dots']==edge:
                        edge = edg['index']
            del self.edges[edge]
        except:
            print("no this edge")

在上面的代碼中,可以看到初始化函數被預設瞭一些值,這點與普通函數並無二致。我們可以看到,默認插入的兩個曲面分別是平面[(0,-1),(0,1)]和弧面[(0,1),(0,-1),(1/2,0)],可見默認生成一個平凸鏡。

成員變量self.edges即光學表面的列表,每個光學表面有兩個參數,分別是索引index和點集dots。由此前的光學抽象可知,當點對中有兩個點的時候,代表平面;有三個點的時候,代表弧面。

方法insertEdge為插入一個光學表面,其中,編號為這個光學表面在self.edges中的索引號;delEdge顧名思義為刪除某個光學表面。如果傳入的edge為一個列表,則說明傳入的是一個參數確定的曲面,此時通過遍歷self.edges找到這個表面,並得到其索引。

如果傳入的參數為一個單值,那麼說明傳入的是索引號,所以直接刪除即可。

在這個方法中,使用瞭一種新的代碼塊try:...except...,這是一種異常機制,即嘗試運行try:塊中的代碼,如果運行失敗,則執行except。如果我們沒能執行成功delEdge,則說明我們輸入的表面並不在這個光學元件中,所以輸出"no this edge"

這好像是第一次看到print這個命令呢,一般來說這應該是最先接觸到的函數,畢竟對於大多數程序員來說,敲下的第一行代碼就是

print("hello world")
print('hello world')

同時,我們除瞭數值類型之外,又認識瞭另一種數據類型,即字符。在python中,可以通過雙引號或者單引號來表示單個字符或者字符串。即上述的hello world代碼中,兩行均正確,而且沒什麼區別。

現在,我們已經寫瞭一個類,於是可以創建一個對象,在命令行中輸入:

>>> from Opti import Opti
>>> Opti.__name__   #這是什麼鬼
'Opti'
>>> x = Opti()      #創建對象,由於未輸入參數,故皆為默認值
>>> x.edges         #現實類成員
[{'index': 0, 'dots': [(0, -1), (0, 1)]}, {'index': 1, 'dots': [(0, 1), (0, -1), (0.5, 0)]}]
>>> x.delEdge(1)    #調用類方法
>>> x.edges         #果然少瞭一個邊
[{'index': 0, 'dots': [(0, -1), (0, 1)]}]
>>> x.delEdge(1)    #刪除不存在的邊是不可能的
no this edge
>>>

首先,from Opti import Opti的這兩個Opti並不相同,前者代表包`Opti.py’,後者代表Opti.py中的類’Opti’,import之後便可以調用瞭。

然後出現瞭一個比較吊詭的事情,我們在類中並沒有定義__name__,然而調用之後卻有值產生。

請勿驚慌,其實是老熟人瞭。可以將__name__理解為python內部的內置屬性,當我們直接執行某一.py文件時,這個__name__的值為__main__,否則的話就是類的名字。所以,到這個時候,我們似乎應該能明白入口函數的真正意義瞭吧。

繼續向下,幾乎所有的事情就都不出所料瞭。

以上就是python光學仿真面向對象光學元件類的實現的詳細內容,更多關於python光學元件類的實現的資料請關註WalkonNet其它相關文章!

推薦閱讀: