Pisanie skryptów w środowisku Mechanical nie musi ograniczać się do prostej automatyzacji czynności niezbędnych do wykonania analizy MES. Dzięki narzędziom takim jak np. ResultReader możemy uzyskać dostęp do surowych wyników w pliku RST i przetworzyć wyniki w sposób specyficzny dla naszych potrzeb, co zwykle wymaga implementacji szeregu funkcji matematycznych. Niniejszy artykuł przybliży dwa zaimplementowane w Mechanical API obiekty, których wykorzystanie pozwoli zaoszczędzić nieco czasu: Vector3D i Matrix4D.
Z uwagi na zakres zagadnienia, artykuł został podzielony na 3 części, z których każda porusza kolejny etap pracy:
Część I – Obiekty Vector3D i Matrix4D: zawiera krótkie wprowadzenie do programowania obiektowego – jak je tworzyć i jak z nich korzystać.
Część II – Transformacja koordynat do lokalnego układu współrzędnych: pełen opis przykładu kodu transformującego koordynaty punktu 3D do lokalnego układu współrzędnych z wykorzystaniem opisanych wcześniej obiektów Vector3D i Matrix4D.
Część III – Odczyt danych z pliku RST i transformacja tensora naprężeń
Odczyt danych z pliku RST i transformacja tensora naprężeń
Opis procesu odczytu danych z pliku RST (na bazie elementów powłokowych) oraz ich transformacji do lokalnego układu współrzędnych z wykorzystaniem obiektów Vector3D i Matrix4D.
Odczyt danych z pliku RST
Najprostszym sposobem na uzyskanie danych numerycznych, jest utworzenie obiektu typu Result, a następnie dostęp do danych z wykorzystaniem własności PlotData. Metoda ta wymaga uprzedniego utworzenia i konfiguracji odpowiednich obiektów. Proces ten jest intuicyjny, ale w niektórych sytuacjach może wymagać utworzenia dużej ilości obiektów.
W sytuacjach, kiedy zależy nam na dobrej optymalizacji możemy pominąć tworzenie obiektów typu Result i odczytać dane bezpośrednio z pliku RST. Metoda ta jest szybsza, ale bardziej wymagająca od strony przetwarzania danych. W tym akapicie postaram się przybliżyć kwestię transformacji tensora naprężeń do lokalnego układu współrzędnych. W artykule będę posiłkował się prostą analizą wykonaną w ANSYS Mechanical 2023R2.
Zacznijmy od odczytu danych z pliku RST – API Mechanical udostępnia klasę ResultReader dostępną z poziomu dowolnej analizy. Aby uniknąć problemów z prawami dostępu, klasę najlepiej inicjalizować w bloku with:
meshData = DataModel.MeshDataByName('Global')
|
Powyższy kod tworzy obiekt umożliwiający dostęp do naprężeń (S). Metoda SelectComponents umożliwia wybranie interesujących nas komponentów (w tym przypadku wszystkich). Istotna jest kolejność symboli komponentów w liście. Na tym etapie natrafiamy na pierwszy element, który może sprawić trudności mniej zaawansowanym użytkownikom.
vs = stress.GetElementValues(eList, False) |
Powyższa metoda pobiera komponenty naprężeń wg. listy sComponents dla elementów, których ID znajdują się w zmiennej eList. Wynikiem jest lista, która zawiera wartości z podziałem na elementy, warstwy, węzły oraz komponenty (w tej kolejności). Aby lepiej zobrazować kolejność wartości posłużę się przykładem. Lista wynikowa dla jednego elementu powłokowego definiowanego przez 4 węzły zawiera 72 wartości: 1 element x 3 warstwy x 4 węzły x 6 komponentów = 72.
Dla przejrzystości, zamieszczamy pełną strukturę listy:
i | ID | Layer | Node | S | i | ID | Layer | Node | S | i | ID | Layer | Node | S |
0 | 1 | Bottom | N1 | X | 24 | 1 | Top | N1 | X | 48 | 1 | Middle | N1 | X |
1 | 1 | Bottom | N1 | Y | 25 | 1 | Top | N1 | Y | 49 | 1 | Middle | N1 | Y |
2 | 1 | Bottom | N1 | Z | 26 | 1 | Top | N1 | Z | 50 | 1 | Middle | N1 | Z |
3 | 1 | Bottom | N1 | XY | 27 | 1 | Top | N1 | XY | 51 | 1 | Middle | N1 | XY |
4 | 1 | Bottom | N1 | YZ | 28 | 1 | Top | N1 | YZ | 52 | 1 | Middle | N1 | YZ |
5 | 1 | Bottom | N1 | XZ | 29 | 1 | Top | N1 | XZ | 53 | 1 | Middle | N1 | XZ |
6 | 1 | Bottom | N2 | X | 30 | 1 | Top | N2 | X | 54 | 1 | Middle | N2 | X |
7 | 1 | Bottom | N2 | Y | 31 | 1 | Top | N2 | Y | 55 | 1 | Middle | N2 | Y |
8 | 1 | Bottom | N2 | Z | 32 | 1 | Top | N2 | Z | 56 | 1 | Middle | N2 | Z |
9 | 1 | Bottom | N2 | XY | 33 | 1 | Top | N2 | XY | 57 | 1 | Middle | N2 | XY |
10 | 1 | Bottom | N2 | YZ | 34 | 1 | Top | N2 | YZ | 58 | 1 | Middle | N2 | YZ |
11 | 1 | Bottom | N2 | XZ | 35 | 1 | Top | N2 | XZ | 59 | 1 | Middle | N2 | XZ |
12 | 1 | Bottom | N3 | X | 36 | 1 | Top | N3 | X | 60 | 1 | Middle | N3 | X |
13 | 1 | Bottom | N3 | Y | 37 | 1 | Top | N3 | Y | 61 | 1 | Middle | N3 | Y |
14 | 1 | Bottom | N3 | Z | 38 | 1 | Top | N3 | Z | 62 | 1 | Middle | N3 | Z |
15 | 1 | Bottom | N3 | XY | 39 | 1 | Top | N3 | XY | 63 | 1 | Middle | N3 | XY |
16 | 1 | Bottom | N3 | YZ | 40 | 1 | Top | N3 | YZ | 64 | 1 | Middle | N3 | YZ |
17 | 1 | Bottom | N3 | XZ | 41 | 1 | Top | N3 | XZ | 65 | 1 | Middle | N3 | XZ |
18 | 1 | Bottom | N4 | X | 42 | 1 | Top | N4 | X | 66 | 1 | Middle | N4 | X |
19 | 1 | Bottom | N4 | Y | 43 | 1 | Top | N4 | Y | 67 | 1 | Middle | N4 | Y |
20 | 1 | Bottom | N4 | Z | 44 | 1 | Top | N4 | Z | 68 | 1 | Middle | N4 | Z |
21 | 1 | Bottom | N4 | XY | 45 | 1 | Top | N4 | XY | 69 | 1 | Middle | N4 | XY |
22 | 1 | Bottom | N4 | YZ | 46 | 1 | Top | N4 | YZ | 70 | 1 | Middle | N4 | YZ |
23 | 1 | Bottom | N4 | XZ | 47 | 1 | Top | N4 | XZ | 71 | 1 | Middle | N4 | XZ |
Struktura powtarza się dla każdego kolejnego elementu. Należy zaznaczyć, że struktura zmienia się w zależności od ilości węzłów oraz typu elementu. Dodatkowo, dla elementów belkowych znaczenie ma ustawienie opcji Beam Section Results dla obiektu Solution. Dla dociekliwych, pełną dokumentację można znaleźć w pomocy ANSYS.
*Dla osób bez konta ANSYS (Customization Suite 2023 R2 -> ACT Customization Guide for Mechanical -> MECHANICAL FEATURE CREATION -> POSTPROCESSING CAPABILITIES IN MECHANICAL -> Retrieving Mechanical Results More Efficiently).
Wracając do naszego skryptu, dla uproszczenia będziemy operować na wartościach uśrednionych dla elementu (Elemental Mean) w środkowej warstwie pojedynczego elementu. Ułatwi to weryfikację oraz wyeliminuje z kodu niepotrzebne pętle.
Na tym etapie mamy zmienną v, która zawiera listę 72 wartości dla elementu o ID=57.
nCnt = len(e.CornerNodeIds) |
Powyższy skrypt iteruje po komponentach po czym dwukrotnie zawęża listę do wartości dla danych komponentów na wymaganej warstwie. Na początku, zawężamy listę do wartości dla danego komponentu ze wszystkich warstw. Rzut oka na tabelkę i widzimy, że co 6 element w liście reprezentuje wartość danego komponentu. Wyrażenie:
v[0::6] |
pozwala na utworzenie nowej listy każdego 6 elementu zaczynając od indeksu 0. Poniżej prosty przykład obrazujący wybieranie n-tego elementu z listy:
l = range(0,11) |
Odpowiednio – v[1::6] zwróci co 6 wartość poczynając od drugiego komponentu (Y).
Ostatecznie, dla komponentu X otrzymamy listę 12 wartości (dla 4 węzłów oraz 3 warstw). Interesująca nas warstwa jest ostatnia. Musimy również pamiętać, że długość listy zależy od liczby węzłów danego elementu. Jeśli zmienna nCnt zawiera informację o liczbie węzłów to:
#l = ['N1_B', 'N2_B', 'N3_B', 'N4_B', 'N1_T', 'N2_T', 'N3_T', 'N4_T', 'N1_M', 'N2_M', 'N3_M', 'N4_M'] |
Na koniec sumujemy wartości w liście metodą sum oraz dzielimy przez liczbę węzłów. Tak uśrednioną wartość wstawiamy do słownika używając symbolu komponentu jako klucza.
Poniższa tabelka prezentuje porównanie wartości pobranych bezpośrednio z pliku RST z wartościami uzyskanymi z wykorzystaniem obiektów Result (dla elementu o ID=57 załączonego przykładu).
Komponent | ResultReader | Result |
X | -47.4161763191223 | -47.416 |
Y | 7.89010169316312E-20 | 7.8901e-020 |
Z | -1.65947604179382 | -1.6595 |
XY | -0.000511929980348214 | -5.1193e-004 |
YZ | -0.000644275627564639 | -6.4428e-004 |
XZ | -31.5254945755005 | -31.525 |
Transformacja tensora naprężeń
Po odczytaniu wartości naprężeń dla elementu możemy utworzyć obiekt Matrix4D reprezentujący tensor naprężeń w postaci:
Poniższy kod obrazuje wykorzystanie utworzonego wcześniej słownika oraz konstruktora kolumnowego:
sigma = Matrix4D.CreateSystem(
|
Aby uzyskać wartości naprężeń w lokalnym układzie współrzędnych, wykorzystamy macierz obrotu Q. Operacje mnożenia macierzy, które należy wykonać możemy zapisać w następujący sposób:
Biorąc pod uwagę, że tworząc macierz obrotu za pomocą metody CreateSystem tworzymy macierz już transponowaną, musimy ją skopiować i transponować, aby otrzymać oryginalną macierz obrotu.
local_cs = DataModel.GetObjectsByName('newCS_1')[0]
|
Finalny kod
Ostateczny kod możemy podzielić na 2 części, część odczytu danych z pliku RST:
meshData = DataModel.MeshDataByName('Global')
|
Oraz część transformująca naprężenia w zmiennej results:
sigma = Matrix4D.CreateSystem(
|
Autor: Bartosz Płochocki, MESco sp. z o.o.
Zainteresował Cię temat? Zobacz nasze szkolenia poświęcone skryptowaniu na poziomie podstawowym i zaawansowanym.