1599x
001773
2022-10-18

Strumento WebService per il ristagno d'acqua in C#

L'API per RFEM 6, RSTAB 9 e RSECTION si basa sul concetto di servizi web. Per una buona introduzione all'argomento, il seguente articolo spiegherà un ulteriore esempio in C#.

L'esempio rappresenta il calcolo di un carico d'acqua applicato a una trave, che aumenta a causa dell'inflessione della trave's. 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 indeformato
  • Larghezza della zona di alimentazione dell'asta per il carico dell'area
  • Numero del caso di carico del caso di carico a cui deve essere applicato il carico
  • Carico superficiale dell'acqua per m di livello dell'acqua (10000 N/m³)
  • Identificatore utilizzato per identificare i carichi dell'area

Nel programma vengono quindi create le seguenti variabili:


            

doppio h_acqua = 0,1;//altezza dell'acqua sul sistema non deformato in [m]
doppio w_ref = 2;//larghezza di riferimento per il carico superficiale in [m]
int no_carico_carico = 1;//numero del caso di carico a cui è applicato il carico
string water_accu_comment = "accumulo d'acqua";//stringa di identificazione
double std_magnitude = 10000;//carico superficiale per altezza dell'acqua in [N/m^3]


Per quanto riguarda l'attuazione, il programma deve includere i seguenti elementi:

  1. Filtraggio delle aste selezionate
  2. Eliminazione dei carichi d'acqua dalle esecuzioni precedenti
  3. Creazione di nuovi carichi
  4. Avvio del calcolo
  5. Determinazione dell'inflessione
  6. Tornando al passaggio 3 e creando nuovi carichi dalle deformazioni
  7. Iterazioni ripetute 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 poi contenuti nel codice sorgente, che può essere scaricato sotto l'articolo.

1. Filtraggio delle aste selezionate

Innanzitutto, otteniamo informazioni su tutti gli oggetti selezionati utilizzando la funzione get_all_selected_objects. L'array ottenuto contiene elementi del tipo posizione_oggetto, che contengono, ad esempio, il tipo, il numero e il numero dell'oggetto "principale" sovraordinato. Nel ciclo seguente, i numeri di tutti gli oggetti del tipo E_OBJECT_TYPE_MEMBER (cioè tutti i numeri delle aste) vengono estratti e salvati nell'array mem_noes_sel.


            

//recupera gli oggetti selezionati
object_location[] obj_locs = model.get_all_selected_objects();

//estrae le 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;
	}
}


2. Eliminazione dei carichi d'acqua dai cicli 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 di asta per conformità con i numeri di asta selezionati e, se c'è una corrispondenza, il carico dell'asta corrispondente viene eliminato:


            

//elimina tutti i carichi water_accu
//ottiene tutti i numeri del carico 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
	member_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);
				}
			}
		}

		
	}

}


Affinché nessun altro carico dell'asta venga sovrascritto, in un passaggio successivo viene letto l'ultimo numero di carico dell'asta utilizzato:


            

//ottiene l'ultimo numero di caricamento
int no_offset = model.get_nth_object_number(object_types.E_OBJECT_TYPE_MEMBER_LOAD,0, load_case_no) + 1;


Ora, il passaggio successivo consiste nel creare i nuovi carichi delle aste, che fanno già parte del ciclo di iterazione do-while. Questo ciclo è costruito come segue:


            

do
{
	//ripristina la deformazione delta
	delta_def = 0;
	
	//applica il carico
	model.begin_modification("Carichi");
	
    //crea i carichi delle aste per ogni asta
	...
    
    model.finish_modification();
    ...
    
	//calcola il caso di carico
	...


	//recupera le deformazioni finali dell'asta
	...

//criterio di controllo
} while (delta_def > 0,0001);


Per interrompere il ciclo di iterazione, è stata selezionata la modifica della deformazione, che è determinata durante il filtraggio dei risultati. Se tutte le deformazioni deviano dalla deformazione nella corsa precedente di meno di 0,0001 m (cioè 0,1 mm), il loop viene interrotto.

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 nodale aggiuntiva 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 lineare dato in [N/m]:


            

//crea i 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 = Member_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);
    
}


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 successive, i carichi non vengono più eliminati; invece, specificando il numero del carico, vengono sovrascritti quando vengono trasferiti nuovamente. 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, è compreso tra 0 e 100%.

Il prossimo passo è avviare il calcolo. Innanzitutto, viene creato un campo con oggetti del tipo calcola_specifica_caricamento. Questo campo contiene tutti i casi di carico/combinazioni da calcolare. Nel caso in esame, viene creato solo un elemento del tipo di caso di carico con il numero del caso di carico specificato load_case_no:


            

//calcola il caso di carico
calcolare_caricamento_specifico[] csl = new calcolare_caricamento_specifico[1];
csl[0] = new calculator_specific_loading();
csl[0].no = load_case_no;
csl[0].type = case_object_types.E_OBJECT_TYPE_LOAD_CASE;

model.calculate_specific(csl, true);


Ora che i risultati sono disponibili, le deformazioni globali all'inizio e alla fine di ogni asta devono essere filtrate. 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 è leggere la lunghezza dell'asta e confrontare la 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 "Extremes" 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 sono necessari ulteriori filtri:


            

//recupera le deformazioni finali 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;
        }
    }
}


Oltre a determinare le deformazioni, viene calcolata anche la variabile "delta_def", che viene utilizzata per il criterio di arresto. Innanzitutto, viene calcolata la differenza tra il valore di deformazione dell'iterazione precedente (zero all'inizio) e il 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 di carico da un lato e l'iterazione sale fino all'arresto dall'altro. Al termine, 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. Molti progetti diversi possono essere implementati con questo. Il programma mostrato qui in questo articolo include solo un primo passaggio di molti elementi diversi:

  1. Ottieni elementi selezionati
  2. Crea carichi
  3. Ottieni e filtra i risultati
  4. Filtra gli elementi per commento
  5. Elimina elementi

A causa di questa varietà, il programma è destinato anche a fungere da modello per altri progetti.


Autore

Il signor Günthel fornisce supporto tecnico per i clienti di Dlubal Software e si prende cura delle loro richieste.

Download