wikipaom2016:lez11
Differenze
Queste sono le differenze tra la revisione selezionata e la versione attuale della pagina.
Entrambe le parti precedenti la revisioneRevisione precedenteProssima revisione | Revisione precedente | ||
wikipaom2016:lez11 [2016/04/24 11:55] – 221177 | wikipaom2016:lez11 [2016/05/29 10:05] (versione attuale) – [Appendice: Librerie esterne LAPACK e BLAS] ebertocchi | ||
---|---|---|---|
Linea 1: | Linea 1: | ||
+ | ====== Codice FEM Fortran autocostruito ====== | ||
+ | |||
+ | ===== Intro ===== | ||
+ | |||
+ | Cos'è un Sottoprogramma (Subroutine)? | ||
+ | Il linguaggio di programmazione FORTRAN consente di suddividere le operazioni tramite sottoprogrammi che ritornano un risultato al programma chiamante, sviluppando un approccio top-down. L' | ||
+ | |||
+ | |||
+ | ===== Subroutine Forces ===== | ||
+ | |||
+ | Concludiamo con l' | ||
+ | {{ : | ||
+ | riguardo l' | ||
+ | |||
+ | |||
+ | Andiamo a rivedere la subroutine FORCES: | ||
+ | {{ : | ||
+ | |||
+ | questa prende in input: | ||
+ | * IFMAX : il numero di nodi sui quali è definita la forza concentrata; | ||
+ | * IFXY : un vettore di elementi interi che definisce l' | ||
+ | * FXY : matrice di (2, IFMAX) 2 righe e IFMAX colonne, e contiene nella prima riga le componenti di forza concentrata in direzione x e nella seconda le componenti di forza concentrate in direzione y; | ||
+ | * NODES : numero di nodi della struttura e serve principalmente a definire la dimensione del vettore FORCE che contiene il termine noto dell' | ||
+ | * FORCE : vettore dei termini nodi che contiene 2*NODES elementi, dove 2 è il numero di gdl al nodo. | ||
+ | |||
+ | Abbiamo poi un ciclo DO che scorre sul IFMAX-esimo nodo caricato, definendo il gdl globale associato allo spostamento (o forza) x //[*2-1]// o y //[*2]// del nodo considerato.\\ Andiamo sul vettore dei termini noti, sulla posizione associata al gdl x definiamo il contenuto della cella alla posizione INDX del vettore dei termini noti FORCE, lo defniamo come elemento (1, | ||
+ | Stessa cosa per la componente INDY del vettore delle forze, è definita uguale al carico in y fornito dall' | ||
+ | |||
+ | |||
+ | |||
+ | Se vogliamo fare un confronto aprendo il file mesh_tria_comp.dat i nodi caricati sono:\\ | ||
+ | {{ : | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | se I10=2 uso la seconda terna di dati, per I10=3 passerò alla terza terna.\\ | ||
+ | \\ | ||
+ | Da qualche altra parte, al di fuori di questa subroutine, il vettore FORCES viene inizializzato a valori nulli perchè in questa subroutine vengono definiti i termini del vettore FORCE solo ove non sono nulli, quindi ci sarà una SUBROUTINE CLEAR chiamata sul vettore FORCE e li verrà azzerato il vettore, per poi andare a definire tutti i termini non nulli dentro al vettore dei carichi esterni. | ||
+ | |||
+ | {{ : | ||
+ | Ovviamente sarebbe molto più leggibile il codice se questo call clear fosse stato inserito dentro la subroutine che crea il vettore FORCE. | ||
+ | |||
+ | =====Subroutine Cnstng====== | ||
+ | La SUBROUTINE successiva CNSTNG, definisce il vincolamento. | ||
+ | {{ : | ||
+ | Prende in input una matrice | ||
+ | |||
+ | Ad esempio nel nostro caso dal file mesh_tria_comp.dat | ||
+ | {{ : | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | \\ | ||
+ | ovviamente lo spostamento in x quando utilizziamo un carrello in dir. y non viene utilizzato. | ||
+ | |||
+ | Questa subroutine parte come un selettore, innanzi tutto c'è un ciclo DO che scorre sui nodi vincolati ( I10=1, ICNMAX ) e si va a fare un test, si va a vedere cosa c'è scritto sulla seconda riga di ICNXY in corrispondenza dello specifico vincolo.\\ | ||
+ | Il 2 presente in ICNXY, specifica che il parametro che deve essere considerato è la seconda riga, ovvero quella corrispondente al tipo di vincolo da applicare, che può essere 1, 2 o 3 a seconda che si tratti di un carrello in direzione x, un carrello in direzione y oppure una cerniera. E qui il codice si triforca, se uguale a 1, 2 o 3 salta rispettivamente alle funzioni 100, 200 o 300. | ||
+ | |||
+ | Nel nostro caso il primo nodo vincolato è il nodo con label 1 e lo specifico vincolo è il 2, quindi quando I10=1 si tratta del primo nodo vincolato, trovo nella matrice un 2 e in questo caso salto alla parte di codice con label 200. Nel caso del secondo nodo vincolato , trovo 3, quindi si salterà alla parte di codice con label 300. | ||
+ | |||
+ | Se non salto a 100, 200 o 300 allora il termine ICNXY non vale ne 1, ne 2, ne 3, la cosa non è prevista dal codice e quindi questo emetterà un segnale d' | ||
+ | Se invece rientra in uno di questi tre casi si salterà alla parte di codice corrispondente; | ||
+ | |||
+ | ==== Vincolamento con carrello in direzione x==== | ||
+ | |||
+ | Vediamo nel dettaglio cosa succede se rientro nel caso 1 con vincolamento in direzione x | ||
+ | {{ : | ||
+ | Prima cosa, si va a ricavare il g.d.l. associato al vincolo specifico, in particolare stiamo parlando dello spostamento in dir. X di un nodo, il nodo è quello che trovo nella matrice ICNXY(1, I10) riga 1 colonna I10 [che nel caso specifico è il nodo 1, il primo dei tre nodi vincolati].\\ | ||
+ | Numero del nodo*2-1 è la mappatura usata per costruire il vettore delle incognite ed è il g.d.l dello spostamento lungo x del nodo 1. Una volta identificato questo primo g.d.l INDX vado a scorrere, tramite ciclo DO con un indice I110 che va da 1 a 2*NODES, quindi scorre su tutti i g.d.l.\\ | ||
+ | Successivamente si opera sulla matrice rigidezza , STRUTK, che ha riga associata al g.d.l ( INDX ) da vincolare, e colonna I110 che scorre su tutti i g.d.l. Tutti gli elementi della INDX-esima riga associata ai g.d.l da vincolare vengono posti = 0 .\\ | ||
+ | E quindi questa istruzione ripetuta all' | ||
+ | Da notare che FORCE(I110) è di volta in volta il primo elemento fino al 2*NODES-esimo elemento di FORCE e viene ridefinito come se stesso meno l' | ||
+ | [Nel nostro caso è un valore imposto di spostamento nullo 0. , ma potrebbe essere anche uno spostamento non omogeneo, ad es. impongo che un certo nodo si muova di 1mm ( o di una quantità a piacere)].\\ | ||
+ | Effettuata la moltiplicazione, | ||
+ | |||
+ | Successivamente: | ||
+ | {{ : | ||
+ | finite le operazioni che richiedono un ciclo, vado a scrivere dunque 1 sul termine diagonale della matrice di rigidezza e vado a scrivere lo spostamento imposto sull' | ||
+ | {{ : | ||
+ | |||
+ | ==== Vincolamento con carrello in direzione y ==== | ||
+ | |||
+ | Stessa cosa se il vincolo è un carrello in dir. Y semplicemente al posto di INDX ( g.d l in x associato a un dato nodo ) utilizzeremo INDY che è il g.d.l y associato al suddetto noto e la costruzione è sostanzialmente analoga: | ||
+ | {{ : | ||
+ | uniche differenze sono il fatto che vado a prendere da CNXY(2,I10) invece del primo elemento che è lo spostamento in x imposto, prendo la seconda riga che contiene gli spostamenti y imposti.\\ | ||
+ | Ricordiamo che si hanno più vincoli, l' | ||
+ | Se la procedura è ben definita cambiando l' | ||
+ | |||
+ | ==== Vincolamento con cerniera ==== | ||
+ | |||
+ | Ultimo caso, la cerniera, semplicemente sarà la combinazione dell' | ||
+ | {{ : | ||
+ | |||
+ | =====Subroutine Vincola====== | ||
+ | |||
+ | Quindi la SUBROUTINE CNSTNG | ||
+ | sostanzialmente implementa il vincolamento in 3 ipotesi, carrello x, carrello y e cerniera, se volessimo qualcosa di più semplice e leggibile, è stata creata una nuova SUBROUTINE ' | ||
+ | {{ : | ||
+ | In pratica prende in input: | ||
+ | * la matrice STRUTK; | ||
+ | * N ( n°di g.d.l pari a 2*NODES); | ||
+ | * FORCES ( il vettore dei termini noti ); | ||
+ | * (g.d.l da vincolare); | ||
+ | * V (valore specifico). | ||
+ | Presenta un ciclo DO con l' | ||
+ | Per J=1, N provvedo ad annullare l' | ||
+ | A questo punto il termine diagonale della riga lo pongo = 1 e la componente I-esima del vettore dei termini noti = allo spostamento imposto V.\\ | ||
+ | Avendo già sistemato FORCE ( I ) = V il valore per il termine I-esimo del vettore dei termini noti, non vorrò più operare su quell' | ||
+ | |||
+ | |||
+ | =====Subroutine Gauss====== | ||
+ | |||
+ | {{ : | ||
+ | |||
+ | Una volta che ho assemblato la matrice rigidezza e l'ho vincolata posso passare alla procedura di soluzione. Non mi resta che chiamare il solutore di equazioni lineari ( CALL GAUSS ) , grazie al quale otterremo i risultati per soluzione Gaussiana, triangolarizzando la matrice di sistema e poi risolvendola.\\ N.B. Non implementare mai questo tipo di risolutore, lo hanno fatto altri con più esperienza, basta copiare/ | ||
+ | Esistono molteplici librerie di codice, ad esempio: | ||
+ | * LAPACK ' Linear Algebra Package'; | ||
+ | * BLAS ' Basic Linear Algebra Subprograms' | ||
+ | Le Blas sono operazioni più semplici come definizione di una matrice come combinazione lineare di altre 2. Si suddividono in 3 LEVEL in dipendenza del peso computazionale delle stesse operazioni. | ||
+ | |||
+ | Una volta lanciato CALL GAUSS questo restituirà il vettore degli spostamenti nodali incogniti UV. | ||
+ | CALL GAUSS prende per input: | ||
+ | * 2*NODES : i g.d.l; | ||
+ | * STRUTK : la matrice di rigidezza; | ||
+ | * FORCES : il vettore dei termini noti; | ||
+ | e restituisce in output: UV : vettore contenente gli spostamenti nodali in x e y;\\ | ||
+ | |||
+ | =====Postprocessor====== | ||
+ | |||
+ | A questo punto ho finito di risolvere il mio sistema, ho trovato dei valori specifici per le incognite, quindi comincio ad emettere fine di output con i risultati. Quindi parto con le istruzioni WRITE che scrivono sull' | ||
+ | {{ : | ||
+ | ovvero apri come unità 2 il file che ha questo, lo status unknown vuol dire "se non esiste lo creo se esiste lo sovrascrivo" | ||
+ | Comincio a scrivere sui file di output per I20 che va dal 1° all' n-esimo nodo; | ||
+ | * I20 : il numero del nodo | ||
+ | * UV(I20*2-1) : lo spostamento lungo x con gdl I20*2-1 | ||
+ | * UV(I20*2) : lo spostamento lungo y con gdl I20*2 | ||
+ | {{ : | ||
+ | Trovati gli spostamenti si potrebbe andare a calcolare le tensioni, che magari ci interessano; | ||
+ | Allora scrivo a video che seguiranno i valori tensionali. Ho un ciclo DO che scorre sugli elementi in questo ciclo che si chiude come tutti i cicli do con il numero ( label ) corrispondente seguito da continue. Vado a ricavare le coordinate x ( per ogni specifico elemento, quindi per ogni elemento dell' | ||
+ | 1° elemento di DELTAEL ( vettore degli spostamenti sull' | ||
+ | Si procede con un prodotto tra matrice e vettore ( DELTAEL qui è un vettore, ma ha solo un indice quindi solo una dimensione), | ||
+ | Il vettore SIGMAEL che è di 3 elementi conterrà come primo elemento la sigma x, come secondo elemento la sigma y e come terzo la tau xy. Prima di scrivere a video queste quantità, vado a calcolare la tensione ideale (equivalente) secondo Von Mises SIGID1 , come: | ||
+ | {{ : | ||
+ | e questa è la tesione ideale secondo Von Mises.\\ | ||
+ | Infine : WRITE(2,*) : stampo a video l' | ||
+ | Possiamo a questo punto lanciare il programma da terminale con il comando: | ||
+ | {{ : | ||
+ | |||
+ | =====Analisi dei risultati====== | ||
+ | |||
+ | Ricordiamo di seguito la struttura analizzata in partenza: | ||
+ | {{ : | ||
+ | |||
+ | Il carico distribuito applicato alla struttura può essere pensato come applicato concentrato sui nodi utilizzando una riduzione secondo aree di influenza. | ||
+ | Lo spessore della struttura non è considerato in questo codice, quindi ogni risultato è normalizzato a spessore unitario. | ||
+ | {{ : | ||
+ | La cerniera applicata nel nodo 2 è soggetta ad una reazione vincolare nulla lungo l'asse x, ma è necessaria per evitare una traslazione indefinita dell' | ||
+ | La cerniera del sistema trattato rappresenta un vincolo di posizionamento: | ||
+ | I risultati restituiti dal programma Fortran autocostruito sono i seguenti: | ||
+ | {{ : | ||
+ | \\ | ||
+ | Spostando il vincolo di posizionamento in un diverso nodo, lo stato tensionale non cambia, mentre la soluzione degli spostamenti nodali differisce di un moto di corpo rigido. | ||
+ | {{ : | ||
+ | Gli spostamenti nodali con ordine di grandezza di almeno 7 volte inferiore a quello della norma del vettore degli spostamenti nodali sono da considerarsi nulli; la restituzione da parte del programma di un risultato numerico, sebbene molto piccolo, è da imputarsi ad errori di calcolo o arrotondamento. | ||
+ | Gli spostamenti nodali mostrano come la struttura, sottoposta al carico, si abbassi e si allarghi per effetto Poisson (gli spostamenti lungo x valgono 0,3 volte gli spostamenti lungo y, proprio come previsto per un acciaio). | ||
+ | Le reazioni vincolari non vengono calcolate dal programma: cioè è dovuto al fatto che la matrice rigidezza pre-vincolamento (necessaria per il calcolo delle reazioni vincolari) non è stata mantenuta in memoria, bensì sovrascritta durante il calcolo. | ||
+ | |||
+ | ---- | ||
+ | =====Appendice: | ||
+ | [[https:// | ||
+ | |||
+ | Esistono in rete delle librerie di riferimento in cui è possibile trovare molti programmi svolti.\\ | ||
+ | Le più famose sono: | ||
+ | * LAPACK (Contiene operazioni matriciali, calcolo di rango, determinante ecc.); | ||
+ | * BLAS (contenente operazioni matriciali). | ||
+ | LAPACK (Linear Algebra PACKage) è un insieme di librerie software usate per effettuare calcoli scientifici ad alto livello. Tali librerie sono state scritte in Fortran 77 e sono di dominio pubblico, anche se esistono delle personalizzazioni di queste librerie a pagamento. L' | ||
+ | |||
+ | BLAS (Basic Linear Algebra Subprograms) sono routine standard che forniscono elementi di base per l’esecuzione delle operazioni su vettori e matrici. Il Livello 1 di BLAS esegue operazioni scalari, vettoriali e vettore-vettore, | ||
+ | |||
+ | Esempio di utilizzo librerie Lapack, che utilizza la subroutine ZGEEV: | ||
+ | |||
+ | | ||
+ | c finding the eigenvalues of a complex matrix using LAPACK | ||
+ | | ||
+ | c declarations, | ||
+ | complex*16 A(3,3), b(3), DUMMY(1,1), WORK(6) | ||
+ | integer i, ok | ||
+ | c define matrix A | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | c | ||
+ | c find the solution using the LAPACK routine ZGEEV | ||
+ | call ZGEEV(' | ||
+ | $WORK,ok) | ||
+ | c | ||
+ | c parameters in the order as they appear in the function call | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | c | ||
+ | c output of eigenvalues | ||
+ | if (ok .eq. 0) then | ||
+ | do i=1, 3 | ||
+ | | ||
+ | enddo | ||
+ | else | ||
+ | write (*,*) "An error occured" | ||
+ | endif | ||
+ | end | ||
+ | \\ | ||
+ | Compilare il codice di cui sopra,con il seguente comando(previa installazione delle librerie lapack):\\ | ||
+ | '' | ||
+ | |||
+ | **Nota**: quando utilizziamo delle librerie esistenti bisogna stare attenti a tutte le variabili e subroutine definite ma che sono sovrabbondanti per i nostri scopi, quindi si preferisce richiamare il programma già compilato con il comando '' | ||
+ | Nel nostro esempio si può vedere nella subroutine ZGEEV le molteplici variabili che sono definite: [[http:// | ||
+ | Questo è il file dell' | ||
+ | |||
+ | |||
+ | \\ | ||
+ | \\ | ||
+ | ---- | ||
+ | Stat | ||
+ | ^ Autore | ||
+ | | F_S | a | | ||
+ | | G_M | a | | ||
+ | | M_G | a | | ||
+ | | MD_R | a | | ||
+ | | Riga_tot | ||
+ | |||
+ | ---- | ||
+ | |||
+ | FIXME | ||
+ | Comando per lanciare mentat | ||
+ | mentat2013.1 -ogl -glflush | ||
+ | FIXME | ||
+ | |||
+ | ~~DISCUSSION~~ |