iT邦幫忙

2022 iThome 鐵人賽

DAY 26
0

今天承接[Day25]的內容,說明程式碼的內容。

Helper Function

首先,我們需要一些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)

PickEntities

base.PickEntities可以讓我們藉由GUI介面來選取Entity,一般需要三個參數,decktypesinitial_type

其中types為可以選取的Entity種類,而initial_type為一開始預設的Entity種類。

這裡我們使用base.PickEntities來選取想要的SolidFacet Entity,並命名一個變數picked_sfs來接收回傳值。如果有選到的話會回傳一個含有選擇SolidFact Entitylist,如果沒有選到的話會回傳一個空的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)

_type

參考[Day25]的說明,我們可以建立:

  • _type_mappings_tetra來對應TETRA四個面的node順序。
    tetra_mappings

  • _type_mappings_hexa來對應HEAX六個面的node順序。
    hexa_mappings

  • _index_mappings來對應TRI3QUAD4TRI6QUAD8等不同shell,所需產生segment(s)node順序。

index_mappings

此外,我們也在這裡建立了四組下面會用到的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')

From node to shell

  • 建立兩個list,命名為selected_shellsto_dels,分別來收集符合條件及不符合條件的shell
  • 對所有選取的solidfacet打一個迴圈,針對每一個solidfacet Entity
    • 透過solidfacet.get_entity_values 取得其EID
    • 透過EID取得其Solid Entity
    • 透過solid.get_entity_values 取得solid_node_keyscard_values,並將其轉換為list存入solid_node_ents,方便後面做indexing操作時使用。
    • 透過solid.get_entity_values 取得type,存入solid_type
    • 透過Entity._type取得_type存入s_type
    • 藉由solid_type,判斷此EntityTETRAHEXA,分別取得其facet_seq(該SolidFacet面的node順序)及shell_node_keys。如果既不是TETRA也不是HEXA,則raiseNotSupportedElemTypeError
    • 透過一個set comprehensions收集所有的node Entity並存入f_node_ents。注意此處的index-1是因為Python的index0開始,而我們在_type_mappings_tetra_type_mappings_hexanode順序是由1開始。
    • 透過base.CreateShellsFromSolidFacets搭配skin可以產生該SolidFacet Entity的所有shell Entity
    • 建立一個listselected_shellsto_dels,分別來收集符合條件及不符合條件的shell
    • 對所有產生的shell打一個迴圈,針對每一個shell Entity
      • 透過shell.get_entity_values 取得其shell_node_keyscard_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)

From shell to refined segment

  • 建立一個set,命名為segments來收集建立的segment
  • selected_shells打一個迴圈,針對每一個shell Entity
    • 透過shell.get_entity_values 取得solid_node_keyscard_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數僅可能為3468,故solid_node_keys是足以覆蓋這些可能性的。
  • 再來一點是使用Entity.get_entity_values時,如果所想getkey是不存在的話,並不會報錯。

所以可以說,我們取巧的使用了solid_node_keys的值。

這樣的code好嗎?我們不置可否。coding的時候,手指總是會展現當下最直接想到的解法。

我們是在已經寫完code,於code review時才發現,原來我們的第一個直覺是這樣子做呀!覺得滿有趣的,所以決定維持現有的code,不作更改。

Code

本日程式碼傳送門(與Day25相同)


上一篇
[Day25] - 建立Solid Element的Segment(1)
下一篇
[Day27] - 快速建立1st Order Solid Elements的Segment Contact
系列文
或躍在淵的CAE: 讓咱們用Python會一會ANSA + LS-DYNA30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言