今天我們來做個housekeeping,整理一下程式。
首先將邊界條件及初始速度,獨立出各自的creator function,放於creators.py中,過程與其它creator function類似,不再贅述。
#creators.py
def create_boundary_spc(fields, deck=None):
deck = deck or constants.LSDYNA
type_ = BCType.BOUNDARY_SPC_SET
return base.CreateEntity(deck, type_, fields)
def create_initial_velocity(fields, deck=None):
deck = deck or constants.LSDYNA
type_ = BCType.INITIAL_VELOCITY_SET
return base.CreateEntity(deck, type_, fields)
接著建立一個card_handlers.py來處理control及database的control card。
由於ANSA內允許創造多個control card或是database的Entity,有時會造成一些輸出的困擾,所以我們會希望盡量減少這類Entity的數量。透過_get_card_ent,我們可以回傳第一個由base.CollectEntities所收集到的Entity。如果ANSA環境內沒有該Entity,我們則建立並回傳。
#card_handlers.py
def get_card_ent(type_, deck=None):
deck = deck or constants.LSDYNA
ents = base.CollectEntities(deck, None, type_)
if ents:
return ents[0]
return base.CreateEntity(deck, type_)
card_handler需要ent、params及deck三個參數。ctrl_params的格式為一list,裡面每個元素為一個含有兩個元素的tuple。tuple的第一個元素為*CONTROL或*DATABASE等keyword後的後綴(不含底線),第二個元素為一dict,內含想設定的參數及其值。這樣的話,我們就可以透過下面的語法來設定control或database等control card。
#*CONTROL_TERMINATION
crtl_ent = get_card_ent(ControlCardType.CONTROL)
ctrl_params = [('TERMINATION', {'ENDTIM': 1.5E-2})]
crtl_ent = card_handler(crtl_ent, ctrl_params)
如Day07提到的,這類型Entity最tricky的地方就是必須要先ON,剩下的就是將params內給定的參數提取出來,放入fields內,最後透過set_entity_values一次設定完成。
#card_handlers.py
def card_handler(ent, params, deck=None):
deck = deck or constants.LSDYNA
fields = {}
for keyword, fields_ in params:
fields[keyword] = 'ON'
ent.set_entity_values(deck, fields=fields)
if fields_:
fields.update({(keyword + '_' + k): v
for k, v in fields_.items()})
ent.set_entity_values(deck, fields=fields)
return ent
建立一個utils.py來儲存一些utility function。
set_deck_to為一helper function,可透過此function將當前ANSA deck設為LS-DYNA。
#utils.py
def set_deck_to(deck=None):
deck = deck or constants.LSDYNA
base.SetCurrentDeck(deck)
set_zoom_all_view呼叫base.ZoomAll,僅是方便視角的呈現。
#utils.py
def set_zoom_all_view():
base.ZoomAll()
set_obj_visible_for_output_deck將需要設定為輸出的contact,邊界條件及初始速度等,打包放入一個函數,方便使用。
#utils.py
def set_obj_visible_for_output_deck(deck=None):
deck = deck or constants.LSDYNA
keys = {LSDYNAType.CONTACT,
BCType.BOUNDARY_SPC_SET,
BCType.INITIAL_VELOCITY_SET}
base.SetEntityVisibilityValues(deck, {key.value: 'on' for key in keys})
將輸出相關設定移至output_file function。
#utils.py
def output_file(filename, deck=None):
deck = deck or constants.LSDYNA
base_dir = Path(__file__).parent
file_str = (base_dir / filename).as_posix()
set_obj_visible_for_output_deck(deck)
base.OutputLSDyna(filename=file_str)
base.SetEntityVisibilityValues(deck, {"CONTACT": "off"})
set_zoom_all_view()
return file_str
將求解相關設定移至run_dyna function。除了檔名路徑是必要輸入參數外,solver的路徑、想使用的cpu數量、想使用的memory大小及額外控制,都透過short circuit技巧給定了預設值。
#utils.py
def run_dyna(file_str,
solver_str=None,
ncpu=None,
memory=None,
d=None):
solver_str = solver_str or f'{Path.home()}/LS-DYNA/13.0/smp-dyna_s'
i = f'i={file_str}'
ncpu = f'ncpu={ncpu}' if ncpu is not None else f'ncpu=8'
memory = f'memory={memory}' if memory is not None else f'memory=1000m'
d = f'd={d}' if d is not None else f'd=nodump'
commands = [solver_str, i, ncpu, memory, d]
subprocess.run(commands)