1598x
001773
18. Oktober 2022

Webservice Tool für Wassersackbildung in C#

Die API für RFEM 6, RSTAB 9 und RSECTION beruht auf dem Konzept der Webservices. Um einen guten Einstieg in die Thematik zu bekommen, soll im folgenden Artikel ein weiterführendes Beispiel in C# erläutert werden.

Das Beispiel ist die Berechnung der Wasserlast auf einem Träger, welche durch die Durchbiegung des Trägers zunimmt. Für eine größere Flexibilität soll der Nutzer den/die Träger auswählen können. Des Weiteren sollen zusätzliche Lasten auf das Dach, welche unabhängig von der Wasserlast im gleichen Lastfall angesetzt werden, nicht verändert werden. Folgende Randbedingungen sind bekannt/werden angesetzt:

  • Wasserhöhe über dem unverformten System
  • Einzugsbreite des Stabes für die Flächenlast
  • Lastfallnummer des Lastfalls, bei dem die Last angelegt werden soll
  • Flächenlast des Wassers pro m Wasserstand (10000 N/m³)
  • Ein Bezeichner zur Identifizierung der Flächenlasten

Im Programm werden daher folgende Variablen angelegt:


            

double h_water = 0.1;   //  height of water over undeformed system in [m]
double w_ref = 2;       //  reference width for surface load in [m]
int load_case_no = 1;   //  number of load case where the load is applied
string water_accu_comment = "water accumulation";  //  identification string
double std_magnitude = 10000;   //  surface load per water height in [N/m^3]


Für die Umsetzung muss das Programm folgende Elemente enthalten:

  1. Selektierte Stäbe herausfiltern
  2. Wasserlasten aus früheren Durchläufen löschen
  3. Neue Lasten anlegen
  4. Berechnung starten
  5. Durchbiegung ermitteln
  6. Wieder zu Schritt 3 und aus Verformungen neue Lasten erzeugen
  7. Iterationen so lange wiederholen, bis ein Grenzwert erreicht wird

Neben diesen Funktionen werden auch die Anbindung an Programm und Modell, diverse try-catch-Blöcke und andere Standardelemente benötigt, auf die hier aber nicht näher eingegangen werden soll. Diese Elemente sind dann im Quellcode enthalten, welcher unterhalb des Artikels heruntergeladen werden kann.

1. Selektierte Stäbe herausfiltern

Zuerst werden Informationen zu allen selektierten Objekten über die Funktion get_all_selected_objects geholt. Das erhaltene Array enthält Elemente vom Typ object_location, welche z.B. den Typ, die Nummer und die übergeordnete "Eltern" Objektnummer enthalten. In der folgenden Schleife werden dann die Nummern aller Objekte vom Typ E_OBJECT_TYPE_MEMBER (also alle Stabnummern) extrahiert und im Array mem_noes_sel gespeichert.


            

//  get selected objects
object_location[] obj_locs = model.get_all_selected_objects();

//  extract members
int[] mem_noes_sel = new int[0];
foreach(object_location obj_loc in obj_locs)
{
	if(obj_loc.type == object_types.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. Wasserlasten aus früheren Durchläufen löschen

Mithilfe der Stabnummern ist es jetzt möglich, die zugehörigen Stablasten aus allen Stablasten herauszufiltern. Dafür wird eine Schleife über die Stablastnummern verwendet. Innerhalb dieser Schleife werden dann die Daten zur Stablast geholt, deren Stabnummern auf Übereinstimmung mit den selektierten Stabnummern überprüft und bei einem Treffer die entsprechende Stablast gelöscht:


            

//  delete all water_accu loads
//      get all memberload numbers
int[] mem_load_noes = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_MEMBER_LOAD, load_case_no);

//  loop through all member loads of the load case
foreach(int mem_load_no in mem_load_noes)
{
	//  get member load
	member_load mem_load = model.get_member_load(mem_load_no, load_case_no);

	if(mem_load.comment == water_accu_comment)
	{
		//  loop through the members of the member load
		for(int i= 0; i < mem_load.members.Length; i++)
		{
			//  loop through the members of the member load
			for (int j = 0; j < mem_noes_sel.Length; j++)
			{
				if(mem_load.members[i] == mem_noes_sel[j])
				{
					//  delete member load
					model.delete_object(object_types.E_OBJECT_TYPE_MEMBER_LOAD, mem_load_no, load_case_no);
				}
			}
		}

		
	}

}


Damit keine anderen Stablasten überschrieben werden, wird als Nächstes die letzte verwendete Stablastnummer ausgelesen:


            

//  get last load number
int no_offset = model.get_nth_object_number(object_types.E_OBJECT_TYPE_MEMBER_LOAD,0, load_case_no) + 1;


Als nächstes erfolgt das Anlegen der neuen Stablasten, was bereits ein Bestandteil der do-while-Iterationsschleife ist. Diese Schleife ist wie folgt aufgebaut:


            

do
{
	//  reset delta deformation
	delta_def = 0;
	
	//  apply load
	model.begin_modification("Loads");
	
    //  create member loads for each member
	...
    
    model.finish_modification();
    ...
    
	//  calculate load case
	...


	//  get member end deformations
	...

//  check criterion
} while (delta_def > 0.0001);


Für den Abbruch der Iterationsschleife, wurde die Änderung der Verformung gewählt, welche beim Filtern der Ergebnisse bestimmt wird. Wenn alle Verformungen gegenüber der Verformung im vorigen Durchlauf weniger als 0,0001m (sprich 0,1mm) abweichen, wird die Schleife abgebrochen.

3. Neue Lasten anlegen

Als Lasttyp wird eine Trapezlast in globaler z-Richtung gewählt, welche am Anfang und am Ende des Stabes unterschiedliche Werte haben kann. Zu beachten ist, dass für alle zu übertragenden Parameter die zugehörigen "Specified" Variable auf "true" gesetzt werden muss. Dies ist notwendig, damit nicht alle Parameter einer Klasse übertragen werden müssen und so die zu übertragende Datenmenge gering gehalten werden kann. Der Wert der Last berechnet sich aus der Anfangshöhe "h_water" (wurde vorgegeben) und der zusätzlichen Knotenverformung an dieser Stelle. Diese Höhe in [m] wird mit "std_magnitude" in [N/m3] und der Einzugsbreite in [m] multipliziert, damit erhält man eine Linienlast in [N/m]:


            

//  create member loads for each member
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);
    
}


Für das Übertragen der Last wird die Funktion set_member_load verwendet. Damit die Lasten zugeordnet werden können, wird als Kommentar die Zeichenkette water_accu_comment verwendet. In den folgenden Iterationen werden die Lasten auch nicht mehr gelöscht, sondern durch Angabe der Lastnummer werden diese beim erneuten Übertragen überschrieben. Der Kommentar wird daher nur zum Filtern und Löschen der Lasten beim erneuten Start der Anwendung benötigt. Des Weiteren wurde für die Angabe der Last ein relativer Bezug gewählt, welcher allerdings von 0-100% reicht.

Im nächsten Schritt wird die Berechnung angestoßen. Dazu wird zuerst ein Feld mit Objekten vom Typ calculate_specific_loading angelegt. Dieses Feld beinhaltet alle zu berechnenden Lastfälle/Lastkombinationen. Im konkreten Fall wird nur ein Element vom Typ Lastfall angelegt mit der vorgegebenen Lastfallnummer load_case_no:


            

//  calculate load case
calculate_specific_loading[] csl = new calculate_specific_loading[1];
csl[0] = new calculate_specific_loading();
csl[0].no = load_case_no;
csl[0].type = case_object_types.E_OBJECT_TYPE_LOAD_CASE;

model.calculate_specific(csl, true);


Nachdem die Ergebnisse zur Verfügung stehen, müssen die globalen Verformungen am Anfang und am Ende jedes Stabes herausgefiltert werden. Über die Funktion get_results_for_members_global_deformations werden zunächst alle globalen Stabverformungen des vorgegebenen Lastfalls und von den selektierten Stäben geholt. Der Aufbau der Ergebnisse ist der gleiche wie in der entsprechenden Tabelle in RFEM 6. Eine Variante ist das Auslesen der Stablänge und der Vergleich der x-Stelle des Ergebnisses. Eine andere Variante nutzt die vergebene Beschreibung (description) und den Fakt, dass nach allen x-Stellen die Extrema folgen. Diese Variante wurde gewählt und wenn das erste Mal "Extrema" in description auftritt, wird der vorige Index verwendet um damit die letzte x-Stelle des Stabes zu finden. Da die erste Stelle des Stabes auch den ersten Eintrag betrifft, muss hier keine weitere Filterung stattfinden:


            

//  get member end deformations
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]);

    //  take start point
    //      calculate delta deformation
    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;
    
    //  get deformation on end point
    for (int j = 0; j < mem_defs_glbl.Length; ++j)
    {
        if (mem_defs_glbl[j].description == "Extremes")
        {
            //      calculate delta deformation
            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;
        }
    }
}


Neben dem Ermitteln der Verformungen, wird auch die Variable "delta_def" berechnet, welche für das Abbruchkriterium verwendet wird. Es wird zuerst die Differenz aus dem Verformungswert der vorigen Iteration (am Anfang null) und dem aktuellen Wert gebildet. Von der Differenz wird der absolute Wert genommen und dann das Maximum gesucht. Wie bereits zuvor in der Beschreibung der Iterationsschleife gezeigt, wird dann bei einem Wert kleiner als 0,0001m (sprich 0,1mm) abgebrochen.

Im angehängten Video können Sie zum einen den Lastfilter und zum anderen die Iterationsschritte bis zum Abbruch sehen. Am Ende wird die gefundene Last dargestellt.

Die Webservice Schnittstelle bietet zahllose Möglichkeiten zur Modifizierung von Elementen in RFEM 6/RSTAB 9, aber auch das Auslesen der Ergebnisse. Damit lassen sich viele verschiedene Projekte realisieren. Das hier gezeigte Programm beinhaltet von vielen verschiedenen Elementen einen ersten Schritt:

  1. Selektierte Elemente holen
  2. Lasten anlegen
  3. Ergebnisse holen und filtern
  4. Elemente nach einem Kommentar filtern
  5. Elemente löschen

Mit dieser Vielfalt soll das Programm auch als Vorlage für andere Projekte dienen.


Autor

Herr Günthel kümmert sich im Kundensupport um die Anliegen unserer Anwender.

Downloads