今天承接[Day25]的內容,說明程式碼的內容。
首先,我們需要一些helper function
,這些都是熟面孔了,不用多作介紹了吧。
#create_solid_segs.py
from datetime import datetime
from enum import Enum
from uuid import uuid4
import ansa
from ansa import base, constants
class NotSupportedElemTypeError(Exception):
pass
class LSDYNAType(str, Enum):
ALL: str = '__ALL_ENTITIES__'
PART: str = 'ANSAPART'
NODE: str = 'NODE'
ELEMENT: str = '__ELEMENTS__'
SOLID: str = 'ELEMENT_SOLID'
SHELL: str = 'ELEMENT_SHELL'
PROPERTY: str = '__PROPERTIES__'
MATERIAL: str = '__MATERIALS__'
SET: str = 'SET'
CONTACT: str = 'CONTACT'
SEGMENT: str = 'SEGMENT'
SOLIDFACET: str = 'SOLIDFACET'
class SolidType(str, Enum):
TETRA: str = 'TETRA'
HEXA: str = 'HEXA'
def _get_u_name():
return datetime.now().strftime('%Y%m%d_%H%M%S') + '_' + str(uuid4().hex)[:6]
def _create_entity(type_, fields, deck=None):
deck = deck or constants.LSDYNA
return base.CreateEntity(deck, type_, fields)
def create_set(entities=None, name=None, deck=None):
deck = deck or constants.LSDYNA
type_ = LSDYNAType.SET
name = name or _get_u_name()
fields = {'Name': name}
set_entity = _create_entity(type_, fields, deck=deck)
if entities is not None:
base.AddToSet(set_entity, entities)
return set_entity
def create_segment(vals=None, deck=None):
deck = deck or constants.LSDYNA
type_ = LSDYNAType.SEGMENT
vals = vals or {}
fields = vals
return _create_entity(type_, fields, deck=deck)
base.PickEntities
可以讓我們藉由GUI介面來選取Entity
,一般需要三個參數,deck
、types
及initial_type
。
其中types
為可以選取的Entity
種類,而initial_type
為一開始預設的Entity
種類。
這裡我們使用base.PickEntities
來選取想要的SolidFacet Entity
,並命名一個變數picked_sfs
來接收回傳值。如果有選到的話會回傳一個含有選擇SolidFact Entity
的list
,如果沒有選到的話會回傳一個空的list
。
我們用一個if
來判斷picked_sfs
是否為空,如果不為空的話,則執行後續建立segment
的程式。
#create_solid_segs.py
if __name__ == '__main__':
deck = constants.LSDYNA
picked_sfs = base.PickEntities(
deck, LSDYNAType.SOLIDFACET, initial_type=LSDYNAType.SOLIDFACET)
if picked_sfs:
main(picked_sfs, deck=deck)
參考[Day25]
的說明,我們可以建立:
_type_mappings_tetra
來對應TETRA
四個面的node
順序。
_type_mappings_hexa
來對應HEAX
六個面的node
順序。
_index_mappings
來對應TRI3
、QUAD4
、TRI6
及QUAD8
等不同shell
,所需產生segment(s)
的node
順序。
此外,我們也在這裡建立了四組下面會用到的keys
。選擇於此處建立的原因是因為,這些keys
在迴圈都是不變的。如果置於迴圈中,只是重複建立這些keys
。
#create_solid_segs.py
def main(sfs, deck=None):
deck = deck or constants.LSDYNA
_type_mappings_tetra = dict(
zip(range(3715, 3719), [(1, 2, 3),
(1, 2, 4),
(2, 3, 4),
(1, 3, 4)]))
_type_mappings_hexa = dict(
zip(range(3715, 3721), [(1, 2, 3, 4),
(1, 2, 5, 6),
(2, 3, 6, 7),
(3, 4, 7, 8),
(1, 4, 5, 8),
(5, 6, 7, 8)]))
_index_mappings = {
3: [(1, 2, 3)],
4: [(1, 2, 3, 4)],
6: [(1, 4, 6), (2, 5, 4), (3, 6, 5), (4, 5, 6)],
8: [(1, 5, 8), (2, 6, 5), (3, 7, 6), (4, 8, 7), (5, 6, 7, 8)]}
shell_tri3_node_keys = ('N1', 'N2', 'N3')
shell_quad4_node_keys = ('N1', 'N2', 'N3', 'N4')
solid_node_keys = ('N1', 'N2', 'N3', 'N4', 'N5', 'N6', 'N7', 'N8')
segment_keys = ('Node 1', 'Node 2', 'Node 3', 'Node 4')
list
,命名為selected_shells
及to_dels
,分別來收集符合條件及不符合條件的shell
。solidfacet
打一個迴圈,針對每一個solidfacet Entity
:
solidfacet.get_entity_values
取得其EID
。EID
取得其Solid Entity
。solid.get_entity_values
取得solid_node_keys
的card_values
,並將其轉換為list
存入solid_node_ents
,方便後面做indexing
操作時使用。solid.get_entity_values
取得type
,存入solid_type
。Entity._type
取得_type
存入s_type
。solid_type
,判斷此Entity
為TETRA
或HEXA
,分別取得其facet_seq
(該SolidFacet
面的node
順序)及shell_node_keys
。如果既不是TETRA
也不是HEXA
,則raiseNotSupportedElemTypeError
。set comprehensions
收集所有的node Entity
並存入f_node_ents
。注意此處的index-1
是因為Python的index
由0
開始,而我們在_type_mappings_tetra
或_type_mappings_hexa
的node
順序是由1
開始。base.CreateShellsFromSolidFacets
搭配skin
可以產生該SolidFacet Entity
的所有shell Entity
。list
,selected_shells
及to_dels
,分別來收集符合條件及不符合條件的shell
。shell
打一個迴圈,針對每一個shell Entity
:
shell.get_entity_values
取得其shell_node_keys
的card_values
,並存入shell_node_ents
。shell_node_ents
皆在f_node_ents
內,如果是則加入selected_shells
,如果不是則加入to_dels
。#create_solid_segs.py
selected_shells, to_dels = [], []
for s in sfs:
eid = s.get_entity_values(deck, ['EID'])['EID']
solid = base.GetEntity(deck, LSDYNAType.SOLID, eid)
solid_node_ents = list(solid.get_entity_values(
deck, solid_node_keys).values()) # for indexing
solid_type = solid.get_entity_values(deck, ['type'])['type']
s_type = s._type
if solid_type == SolidType.TETRA:
facet_seq = _type_mappings_tetra.get(s_type)
shell_node_keys = shell_tri3_node_keys
elif solid_type == SolidType.HEXA:
facet_seq = _type_mappings_hexa.get(s_type)
shell_node_keys = shell_quad4_node_keys
else:
raise NotSupportedElemTypeError
# become set, for later in operator
f_node_ents = {solid_node_ents[i-1] for i in facet_seq}
shells = base.CreateShellsFromSolidFacets(
'skin', ret_ents=True, solids=solid)
for shell in shells:
shell_node_ents = shell.get_entity_values(
deck, shell_node_keys).values()
conds = (shell_node_ent in f_node_ents
for shell_node_ent in shell_node_ents)
if all(conds):
selected_shells.append(shell)
else:
to_dels.append(shell)
set
,命名為segments
來收集建立的segment
。selected_shells
打一個迴圈,針對每一個shell Entity
:
shell.get_entity_values
取得solid_node_keys
的card_values
,並將其轉換為list
存入nodes
,方便後面做indexing
操作時使用(備註
)。node
數量搭配_index_mappings
可以得到該shell Entity
準備產生segment(s)
的node
順序,存入idxes
。idxes is None
,則raise NotSupportedElemTypeError
。list comprehensions
可以取得各segment
所需的node Entity
,存入seqs
。seqs
打一個迴圈,針對每一個seq
:
segment Entity
。segment Entity
加入segments
。set Entity
,將segments
加入該Entity
。shell Entity
。#create_solid_segs.py
segments = set()
for shell in selected_shells:
nodes = list(shell.get_entity_values(
deck, solid_node_keys).values()) # for indexing
n_nodes = len(nodes)
idxes = _index_mappings.get(n_nodes)
if idxes is None:
raise NotSupportedElemTypeError
seqs = [[nodes[_idx-1] for _idx in idxs]
for idxs in idxes]
for seq in seqs:
fields = dict(zip(segment_keys, seq))
segment = create_segment(fields, deck=deck)
segments.add(segment)
segments_set = create_set(segments, deck=deck)
base.DeleteEntity([*selected_shells, *to_dels])
聰明的您可能發現,這邊不是在做shell.get_entity_values
,怎麼會取用solid_node_keys
呢?原因是因為:
shell Entity
,其node
數僅可能為3
、4
、6
及8
,故solid_node_keys
是足以覆蓋這些可能性的。Entity.get_entity_values
時,如果所想get
的key
是不存在的話,並不會報錯。所以可以說,我們取巧的使用了solid_node_keys
的值。
這樣的code好嗎?我們不置可否。coding的時候,手指總是會展現當下最直接想到的解法。
我們是在已經寫完code,於code review時才發現,原來我們的第一個直覺是這樣子做呀!覺得滿有趣的,所以決定維持現有的code,不作更改。