1596x
001773
2022-10-18

Narzędzie Webservice dla zbiorników wodnych w języku C#

API dla RFEM 6, RSTAB 9 i RSECTION opiera się na koncepcji usług sieciowych (Webservice). Aby zapoznać się z tematem, w poniższym artykule omawiamy kolejny przykład w języku C#.

Przykład przedstawia obliczenia obciążenia wodą przyłożonego do belki, które narasta w wyniku ugięcia belki. Aby zapewnić większą elastyczność, użytkownik powinien mieć możliwość wyboru belek. Ponadto nie należy modyfikować dodatkowych obciążeń działających na dach, które są przyłożone do tego samego przypadku obciążenia niezależnie od obciążenia wodą. Znane/stosowane są następujące warunki brzegowe:

  • Poziom wody nad układem nieodkształconym
  • Szerokość strefy, z której zbierane są obciążenia
  • Numer przypadku obciążenia, dla którego ma zostać przyłożone obciążenie
  • Obciążenie powierzchniowe wodą na metr poziomu wody (10000 N/m³)
  • Identyfikator używany do identyfikacji obciążeń powierzchniowych

W programie tworzone są zatem następujące zmienne:


            

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]


W zakresie realizacji program musi zawierać następujące elementy:

  1. Odfiltrowanie wybranych prętów
  2. Usuwanie obciążeń wodą z poprzednich procesów
  3. Tworzenie nowych obciążeń
  4. Rozpoczęcie obliczeń
  5. Wyznaczanie ugięcia
  6. Powrót do kroku 3 i tworzenie nowych obciążeń z odkształceń
  7. Powtarzanie iteracji aż do osiągnięcia wartości granicznej

Oprócz tych funkcji wymagane jest połączenie z programem i modelem, różne bloki try-catch i inne standardowe elementy, które nie zostały tutaj opisane bardziej szczegółowo. Elementy te są następnie zawarte w kodzie źródłowym, który można pobrać poniżej.

1. Odfiltrowanie wybranych prętów

Najpierw uzyskujemy informacje o wszystkich wybranych obiektach za pomocą funkcji get_all_selected_objects. Otrzymana matryca zawiera elementy typu object_location, które zawierają np. typ, numer oraz nadrzędny numer obiektu "nadrzędnego". W poniższej pętli zostaną wyodrębnione i zapisane w matrycy mem_noes_sel numery wszystkich obiektów typu E_OBJECT_TYPE_MEMBER (tzn. numery prętów).


            

//  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. Usuwanie obciążeń wodą z poprzednich procesów

Za pomocą numerów prętów można teraz odfiltrować powiązane obciążenia prętowe ze wszystkich obciążeń prętowych. Zastosowana zostanie pętla nad numerami obciążeń pręta. W ramach tej pętli uzyskujemy dane dla obciążenia pręta, sprawdzamy jego numery prętów zgodnie z wybranymi numerami prętów, a w przypadku dopasowania, odpowiednie obciążenie jest usuwane:


            

//  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);
				}
			}
		}

		
	}

}


Aby nie nadpisywać innych obciążeń pręta, w kolejnym kroku odczytywany jest numer ostatnio użytego obciążenia pręta:


            

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


Kolejnym krokiem jest utworzenie nowych obciążeń pręta, które są już częścią pętli iteracyjnej. Pętla jest zbudowana w następujący sposób:


            

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);


W celu zatrzymania pętli iteracyjnej wybrano zmianę odkształcenia, która jest określana podczas filtrowania wyników. Jeżeli wszystkie odkształcenia odbiegają od odkształcenia z poprzedniego procesu o mniej niż 0,0001 m (tj. 0,1 mm), pętla zostaje zatrzymana.

3. Tworzenie nowych obciążeń

Jako typ obciążenia wybrane jest obciążenie trapezowe działające w globalnym kierunku Z, które może mieć różne wartości na początku i na końcu pręta. Należy pamiętać, że przynależna zmienna "Specified" musi być ustawiona na "prawdziwa" dla wszystkich parametrów, które mają zostać przesłane. Jest to konieczne, aby nie przenosić wszystkich parametrów klasy i aby w ten sposób ilość przesyłanych danych była raczej niewielka. Wartość obciążenia jest obliczana na podstawie wysokości początkowej "h_water" (podana) oraz dodatkowego odkształcenia węzłowego w tym miejscu. Wysokość podaną w [m] należy pomnożyć przez "std_magnitude" w [N/m3] i szerokość strefy podawania podaną w [m], co daje obciążenie liniowe wyrażone w [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);
    
}


Funkcja set_member_load służy do przenoszenia obciążenia. W celu przypisania obciążeń, ciąg water_accu_comment służy jako komentarz. W kolejnych iteracjach obciążenia nie są już usuwane; zamiast tego, poprzez podanie numeru obciążenia, są one nadpisywane podczas ponownego przenoszenia. Komentarz jest zatem potrzebny tylko do filtrowania i usuwania obciążeń po ponownym uruchomieniu aplikacji. Ponadto do określenia obciążenia wybrano względną wartość odniesienia, która jednak mieści się w zakresie 0-100%.

Kolejnym krokiem jest rozpoczęcie obliczeń. Najpierw tworzone jest pole z obiektami typu calculate_specific_loading. W tym polu znajdują się wszystkie przypadki/kombinacje obciążeń, które mają zostać obliczone. W obecnym przypadku tworzony jest tylko jeden element typu przypadku obciążenia o podanym numerze przypadku obciążenia load_case_number:


            

//  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);


Teraz, gdy wyniki są dostępne, należy odfiltrować globalne odkształcenia na początku i na końcu każdego pręta. Funkcja get_results_for_members_global_deformations umożliwia pobranie wszystkich globalnych odkształceń pręta dla określonego przypadku obciążenia i wybranych prętów. Struktura wyników jest taka sama, jak w odpowiedniej tabeli w programie RFEM 6. Jednym z wariantów jest odczytanie długości pręta i porównanie położenia x wyniku. W innym wariancie wykorzystano podany opis oraz fakt, że po wszystkich położeniach x występują wartości ekstremalne. Wybrano drugi wariant i gdy w opisie po raz pierwszy pojawi się „Wartości ekstremalne”, do znalezienia ostatniego położenia x pręta używany jest poprzedni indeks. Ponieważ pierwsze położenie pręta wpływa również na pierwszy wpis, dalsze filtrowanie nie jest tutaj konieczne:


            

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


Oprócz określenia odkształceń obliczana jest również zmienna "delta_def", która jest wykorzystywana jako kryterium zatrzymania. Najpierw obliczana jest różnica między wartością odkształcenia z poprzedniej iteracji (zero na początku) a wartością bieżącą. Wartość bezwzględna jest brana z różnicy, a następnie określane jest maksimum. Jak już pokazano w opisie pętli iteracyjnej, zatrzymujemy się na wartości mniejszej niż 0,0001 m (tj. 0,1 mm).

W filmie dołączonym do tego artykułu można zobaczyć z jednej strony filtr obciążenia, a z drugiej strony iterację. Na końcu wyświetlane jest znalezione obciążenie.

Interfejs webservice oferuje niezliczone możliwości modyfikacji elementów w programie RFEM 6/RSTAB 9, a także odczytywania wyników. Dzięki temu można zrealizować wiele różnych projektów. Program przedstawiony w tym artykule obejmuje tylko pierwszy krok składający się z wielu różnych elementów:

  1. Pobieranie wybranych elementów
  2. Tworzenie obciążenia
  3. Pobieranie i filtrowanie wyników
  4. Filtrowanie elementów według komentarza
  5. Usuwanie elementów

Ze względu na tę różnorodność program może służyć również jako szablon dla innych projektów.


Autor

Pan Günthel zapewnia wsparcie techniczne klientom firmy Dlubal Software i zajmuje się ich zapytaniami.

Pobrane