L'esempio rappresenta il calcolo di un carico d'acqua applicato a una trave, che aumenta a causa dell'inflessione della trave'. Per essere più flessibile, l'utente dovrebbe essere in grado di selezionare le travi. Inoltre, i carichi aggiuntivi che agiscono sulla copertura, che sono applicati allo stesso caso di carico indipendentemente dal carico dell'acqua, non dovrebbero essere modificati. Sono note/applicate le seguenti condizioni al contorno:
- Livello dell'acqua sopra il sistema non deformato
- Larghezza della zona di alimentazione dell'asta per il carico superficiale
- Numero del caso di carico del caso di carico per il quale il carico deve essere applicato
- Carico superficiale dell'acqua per m di livello dell'acqua (10000 N/m³)
- Identificatore utilizzato per identificare i carichi superficiali
Le seguenti variabili sono quindi create nel programma:
- code.sharp#
doppio h_acqua = 0,1;//altezza dell'acqua sul sistema non deformato in [m]
doppio w_ref = 2;//larghezza di riferimento per il carico della superficie in [m]
int caso_carico_no = 1;//numero del caso di carico a cui viene applicato il carico
string water_accu_comment = "accumulo di acqua";//stringa di identificazione
double std_magnitude = 10000;//carico della superficie per altezza dell'acqua in [N/m^3]
- /codice#
Per quanto riguarda l'implementazione, il programma deve includere i seguenti elementi:
- Filtraggio delle aste selezionate
- Eliminazione dei carichi d'acqua dalle esecuzioni precedenti
- Creazione di nuovi carichi
- Avvio del calcolo
- Determinazione dell'inflessione
- Tornando al passaggio 3 e creando nuovi carichi dagli spostamenti generalizzati
- Ripetizione delle iterazioni fino al raggiungimento di un valore limite
Oltre a queste funzioni, sono necessari il collegamento al programma e al modello, vari blocchi try-catch e altri elementi standard, ma non sono descritti in dettaglio qui. Questi elementi sono quindi inclusi nel codice sorgente, che può essere scaricato sotto questo articolo.
1. Filtraggio delle aste selezionate
Innanzitutto, otteniamo informazioni su tutti gli oggetti selezionati utilizzando la funzione get_all_selected_objects. L'array ricevuto contiene elementi del tipo posizione_oggetto, che possono, ad esempio, il tipo, il numero e il numero dell'oggetto "padre" di ordine superiore. Nel seguente ciclo, i numeri di tutti gli oggetti del tipo E_OBJECT_TYPE_MEMBER (cioè tutti i numeri delle aste) sono estratti e salvati nell'array mem_noes_sel.
- code.csharp#
//recupera gli oggetti selezionati
object_location[] obj_locs = model.get_all_selected_objects();
//estrae aste
int[] mem_noes_sel = nuovo int[0];
foreach(posizione_oggetto obj_loc in obj_locs)
{
if(obj_loc.type == tipi_oggetto.E_OBJECT_TYPE_MEMBER)
{
Array.Resize(ref mem_noes_sel, mem_noes_sel.Length + 1);
mem_noes_sel[mem_noes_sel.Length-1] = obj_loc.no;
}
}
- /codice#
2. Eliminazione dei carichi d'acqua dalle esecuzioni precedenti
Tramite i numeri delle aste, è ora possibile filtrare i carichi delle aste associati da tutti i carichi delle aste. Viene utilizzato un ciclo inserito sui numeri di carico dell'asta. All'interno di questo ciclo, otteniamo i dati per il carico dell'asta, controlliamo i suoi numeri dell'asta in conformità con i numeri dell'asta selezionati e, se c'è una corrispondenza, il carico dell'asta corrispondente viene eliminato:
- code.csharp#
//elimina tutti i carichi water_accu
//ottiene tutti i numeri dei carichi delle aste
int[] mem_load_noes = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_MEMBER_LOAD, load_case_no);
//scorre tutti i carichi delle aste del caso di carico
foreach(int mem_load_no in mem_load_noes)
{
//ottiene il carico dell'asta
mem_load mem_load = model.get_member_load(mem_load_no, load_case_no);
if(mem_load.comment == water_accu_comment)
{
//scorre le aste del carico dell'asta
for(int i= 0; i < mem_load.members.Length; i++)
{
//scorre le aste del carico dell'asta
for (int j = 0; j < mem_noes_sel.Length; j++)
{
if(mem_load.members[i] == mem_noes_sel[j])
{
//elimina il carico dell'asta
model.delete_object(object_types.E_OBJECT_TYPE_MEMBER_LOAD, mem_load_no, load_case_no);
}
}
}
}
}
- /codice#
Affinché nessun altro carico dell'asta venga sovrascritto, in un passaggio successivo viene letto l'ultimo numero di carico dell'asta utilizzato:
- code.csharp#
//ottiene l'ultimo numero di carico
int no_offset = model.get_nth_object_number(object_types.E_OBJECT_TYPE_MEMBER_LOAD,0, load_case_no) + 1;
- /codice#
Ora, il passaggio successivo è creare i nuovi carichi delle aste, che fanno già parte del ciclo di iterazione do-while. Questo ciclo è costruito come segue:
- code.csharp#
do
{
//ripristina la deformazione delta
delta_def = 0;
//applica il carico
model.begin_modification("Carichi");
- //crea carichi delle aste per ogni asta
…
- model.finish_modification();
- …
//calcola il caso di carico
…
//ottiene gli spostamenti generalizzati delle estremità dell'asta
…
//verifica il criterio
} while (delta_def > 0.0001);
- /codice#
Per interrompere il ciclo di iterazione, è stata selezionata la modifica della deformazione, che è determinata durante il filtraggio dei risultati. Se tutti gli spostamenti generalizzati deviano dalla deformazione nella corsa precedente di meno di 0,0001 m (cioè 0,1 mm), il loop si interrompe.
3. Creazione di nuovi carichi
Come tipo di carico viene selezionato un carico trapezoidale che agisce nella direzione z globale, che può avere valori diversi all'inizio e alla fine dell'asta. Si noti che la variabile "Specificata" associata deve essere impostata su "true" per tutti i parametri da trasferire. Ciò è necessario per non trasferire tutti i parametri di una classe e per mantenere in questo modo la quantità di dati da trasferire piuttosto bassa. Il valore del carico's è calcolato dall'altezza iniziale "h_water" (è stata specificata) e dalla deformazione aggiuntiva del nodo su questa posizione. L'altezza data in [m] è moltiplicata per "std_magnitude" in [N/m3 ] e la larghezza della zona di alimentazione data in [m], risultando in un carico della linea dato in [N/m]:
- code.csharp#
//crea carichi delle aste per ogni asta
for (int i = 0; i < mem_noes_sel.Length; ++i)
{
Member_load mem_load = new Member_load();
mem_load.no = no_offset + i;
mem_load.comment = water_accu_comment;
mem_load.members = new int[] { mem_noes_sel[i] };
mem_load.distance_a_is_defined_as_relative = true;
mem_load.distance_a_is_defined_as_relativeSpecified = true;
mem_load.distance_a_relative = 0.0;
mem_load.distance_a_relativeSpecified = true;
mem_load.distance_b_is_defined_as_relative = true;
mem_load.distance_b_is_defined_as_relativeSpecified = true;
mem_load.distance_b_relative = 1.0;
mem_load.distance_b_relativeSpecified = true;
mem_load.load_direction = Member_load_load_direction.LOAD_DIRECTION_GLOBAL_Z_OR_USER_DEFINED_W_TRUE;
mem_load.load_directionSpecified = true;
mem_load.load_type = aste_load_load_type.LOAD_TYPE_FORCE;
mem_load.load_typeSpecified = true;
mem_load.load_distribution = Member_load_load_distribution.LOAD_DISTRIBUTION_TRAPEZOIDAL;
mem_load.load_distributionSpecified = true;
- mem_load.magnitude_1 = std_magnitude * ((h_water + mem_end_defs[i,0]) * w_ref);
mem_load.magnitude_1Specified = true;
mem_load.magnitude_2 = std_magnitude * ((h_water + mem_end_defs[i,1]) * w_ref);
mem_load.magnitude_2Specified = true;
model.set_member_load(load_case_no, mem_load);
}
- /codice#
La funzione set_member_load viene utilizzata per trasferire il carico. Per assegnare i carichi, la stringa water_accu_comment viene utilizzata come commento. Nelle iterazioni seguenti, i carichi non saranno più eliminati; invece, specificando il numero del carico, vengono sovrascritti quando vengono trasferiti di nuovo. Il commento è quindi necessario solo per filtrare ed eliminare i carichi al riavvio dell'applicazione. Inoltre, è stato scelto un riferimento relativo per la specifica del carico, che, tuttavia, varia da 0 a 100%.
Il prossimo passo è avviare il calcolo. Innanzitutto, viene creato un campo con oggetti del tipo di calcolo_specifico_caricamento. Questo campo contiene tutti i casi/combinazioni di carico da calcolare. In questo caso, viene creato solo un elemento del tipo di caso di carico con il numero del caso di carico specificato load_case_no:
- code.csharp#
//calcola il caso di carico
calcolare_caricamento_specifico[] csl = nuovo calcolo_caricamento_specifico[1];
csl[0] = newculation_specific_loading();
csl[0].no = carico_caso_no;
csl[0].type = case_object_types.E_OBJECT_TYPE_LOAD_CASE;
model.calculate_specific(csl, true);
- /codice#
Ora che i risultati sono disponibili, gli spostamenti generalizzati globali all'inizio e alla fine di ogni asta devono essere filtrati. La funzione get_results_for_members_global_deformations viene utilizzata per ottenere tutte le deformazioni globali delle aste del caso di carico specificato e delle aste selezionate. La struttura dei risultati è la stessa della tabella corrispondente in RFEM 6. Una variante è la lettura della lunghezza dell'asta e il confronto della posizione x del risultato. Un'altra variante utilizza la descrizione data e il fatto che gli estremi seguono tutte le posizioni x. È stata scelta la seconda variante e quando "Estremi" appare per la prima volta nella descrizione, l'indice precedente viene utilizzato per trovare l'ultima posizione x dell'asta. Poiché la prima posizione dell'asta influisce anche sulla prima voce, qui non è necessario alcun ulteriore filtro:
- code.csharp#
//ottiene gli spostamenti generalizzati delle estremità dell'asta
for (int i = 0; i < mem_noes_sel.Length; ++i)
{
- Members_global_deformations_row[] mem_defs_glbl = model.get_results_for_members_global_deformations(case_object_types.E_OBJECT_TYPE_LOAD_CASE, load_case_no, mem_noes_sel[i]);
- //prende il punto iniziale
- //calcola la deformazione delta
- if (Math.Abs(mem_end_defs[i, 0] - mem_defs_glbl[0].row.displacement_z) > delta_def)
- delta_def = Math.Abs(mem_end_defs[i, 0] - mem_defs_glbl[0].row.displacement_z);
- mem_end_defs[i, 0] = mem_defs_glbl[0].row.displacement_z;
- //ottiene la deformazione sul punto finale
- for (int j = 0; j < mem_defs_glbl.Length; ++j)
- {
- if (mem_defs_glbl[j].description == "Estremi")
- {
- //calcola la deformazione delta
- if (Math.Abs(mem_end_defs[i, 1] - mem_defs_glbl[j - 1].row.displacement_z) > delta_def)
- delta_def = Math.Abs(mem_end_defs[i, 1] - mem_defs_glbl[j - 1].row.displacement_z);
- mem_end_defs[i, 1] = mem_defs_glbl[j - 1].row.displacement_z;
- break;
- }
- }
}
- /codice#
Oltre a determinare gli spostamenti generalizzati, viene calcolata anche la variabile "delta_def", che viene utilizzata per il criterio di arresto. Innanzitutto, viene calcolata la differenza dal valore di deformazione dell'iterazione precedente (zero all'inizio) e dal valore corrente. Il valore assoluto è preso dalla differenza e quindi si trova il massimo. Come già mostrato nella descrizione del ciclo di iterazione, ci fermiamo a un valore inferiore a 0,0001 m (cioè 0,1 mm).
Nel video allegato a questo articolo, puoi vedere il filtro del carico da un lato e l'iterazione aumenta fino all'arresto dall'altro. Alla fine, viene visualizzato il carico trovato.
L'interfaccia WebService offre innumerevoli opzioni per modificare gli elementi in RFEM 6/RSTAB 9, ma anche per leggere i risultati. Con esso si possono realizzare molti progetti diversi. Il programma mostrato qui in questo articolo include solo un primo passaggio di molti elementi diversi:
- Ottieni elementi selezionati
- Crea carichi
- Ottieni e filtra i risultati
- Filtra gli elementi per commento
- Elimina elementi
A causa di questa varietà, il programma è destinato anche a fungere da modello per altri progetti.