1181x
001825
04.09.2024

Service web & API pour l'analyse des phases de construction

Lorsqu'il s'agit de calculer des structures régulières, l'entrée des données n'est souvent pas complexe, mais tout simplement trop longue. L'automatisation de la saisie permet alors de gagner un temps précieux. Dans le cas présent, il s'agit de considérer les étages d'une maison comme des étapes individuelles de construction. La saisie doit être effectuée à l'aide d'un programme C# afin que l'utilisateur n'ait pas à entrer manuellement les éléments des différents étages.

Conditions de participation

Le programme suivant devrait fonctionner dans les condition aux limites suivantes :

  • Les plans des étages sont parallèles au plan x-y
  • Le modèle ne doit pas contenir d'éléments transversaux (par exemple, une surface de façade sur deux étages ou plus)
  • Les fondations et le premier plafond comptent comme étage 0.
  • Les solides ne sont pas pris en compte
  • Seules les surfaces planes sont utilisées
  • L’axe z est dirigé dans la direction de l’accélération de la pesanteur (« vers le bas »)

Principes théoriques

Il résulte des conditions préalables que seules les hauteurs d’étage doivent être indiquées et qu’un élément, un nœud, par exemple, doit alors se trouver avec sa coordonnée z au-dessus de la hauteur d’étage inférieure et au maximum à la hauteur d’étage actuelle.

L'utilisateur doit donc sélectionner un nombre de nœuds représentatifs des hauteurs d’étage. Les coordonnées z et donc les hauteurs d'étage peuvent être déterminées à l'aide d'une boucle.

Les étages sont assignés à tous les nœuds en fonction des hauteurs.

Dans le cas des lignes, les étages des nœuds peuvent être utilisés. Si une ligne commence à un étage et se termine à l’étage supérieur, elle doit être assignée à l’étage supérieur. La ligne est donc assignée à l’étage le plus élevé qui peut être trouvé dans vos nœuds.

Étant donné que les barres reposent sur des lignes et que celles-ci peuvent être assignées devant elles, la barre est assignée au même étage que sa ligne.

Le comportement des surfaces est similaire à celui des lignes. L’étage le plus élevé des nœuds associés est déterminé.

Si tous les éléments d’un étage ont pu être assignés, les phases de construction peuvent être créées à partir des listes d’étages.

Mise en application

Le modèle type de NuGet-Paket Dlubal.RFEMWebServiceLibrary a été utilisé comme base pour le programme. Pour savoir comment l'installer, cliquez ici :

https://github.com/Dlubal-Software/Dlubal_CSharp_Client

Commencez par connecter le modèle actif comme suit :

...
#region Application Settings
try
{
    information_application ApplicationInfo;
    try
    {
        // connects to RFEM6 or RSTAB9 application
        application = new ApplicationClient(Binding, Address);

    }
    catch (Exception exception)
    {
        if (application != null)
        {
            if (application.State != CommunicationState.Faulted)
            {
                application.Close();
            }
            else
            {
                application.Abort();
            }

            application = null;
        }
    }
    finally
    {
        ApplicationInfo = application.get_information();
        Console.WriteLine("Name: {0}, Version:{1}, Type: {2}, language: {3} ", ApplicationInfo.name, ApplicationInfo.version, ApplicationInfo.type, ApplicationInfo.language_name);
    }
    #endregion

    // get active model
    string modelUrl = application.get_active_model();

    // connects to RFEM6/RSTAB9 model
    ModelClient model = new ModelClient(Binding, new EndpointAddress(modelUrl));
...

Pour une meilleure gestion des erreurs, un bloc try-catch est utilisé dans le code source dans la fonction principale. Dans ce bloc, l’application est d'abord connectée, ce qui intervient à son tour dans un bloc try-catch. Une fois l’application connectée, le modèle actuellement actif (au premier plan dans RFEM 6) est connecté à l’aide de la méthode get_active_model. S’il y a des problèmes ici, le bloc try-catch externe prend effet.

Étant donné que tous les nœuds, lignes, barres et surfaces doivent être examinés, il est logique d’obtenir tous les éléments à partir de RFEM. Pour connaître le nombre d’éléments respectifs, les numéros d’objet de toutes les catégories sont d’abord lus. Ci-dessous, les objets respectifs peuvent être transférés à l'aide de leurs numéros :

...
int[] node_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_NODE, 0);
int[] line_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_LINE, 0);
int[] member_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_MEMBER, 0);
int[] surface_nums = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_SURFACE, 0);

//  get all nodes
Console.WriteLine("1. Get all nodes:");
node[] nds = new node[node_nums.Length];
for (int i = 0; i < node_nums.Length; ++i)
{
	nds[i] = model.get_node(node_nums[i]);
	if ((i%10)==0)
		Console.Write("+");

}
Console.WriteLine("");
...

Pour que l’utilisateur ait une vue d’ensemble de la durée du processus, un «+» est écrit tous les 10 éléments dans la ligne de commande.

Avant de transférer d’autres éléments, l’étape suivante consiste à déterminer les hauteurs d’étage. Pour ce faire, les objets sélectionnés doivent d’abord être lus. Le tableau transféré (tableau/vecteur avec des éléments) de type object_location contient tous les objets sélectionnés avec leur type et leur numéro. Les nœuds peuvent ainsi être filtrés en boucle. Tous les nœuds étant déjà disponibles, il est possible de déterminer les coordonnées z de ces nœuds dans la même boucle. Une boucle passe par les objets sélectionnés et dès qu'un élément est de type nœud, ce nœud est recherché parmi les nœuds et sa coordonnée z est ensuite entrée dans le champ floor_heights. Pour chaque coordonnée trouvée, un élément est ajouté au tableau :

...
Console.WriteLine("2. Get selected nodes");
// get all selected objects
object_location[] obj_locs = model.get_all_selected_objects();

//  get all selected node numbers
double[] floor_heights = new double[1];
foreach (object_location obj in obj_locs)
{
	if (obj.type == object_types.E_OBJECT_TYPE_NODE)
	{
		for (int i = 0; i < nds.Length; ++i)
		{
			if (nds[i].no == obj.no)
			{
				floor_heights[floor_heights.Length - 1] = nds[i].coordinate_3;
				Array.Resize(ref floor_heights, floor_heights.Length + 1);
				break;
			}
		}
	}
}
Array.Resize(ref floor_heights, floor_heights.Length - 1);

//  sort array
// z-axis is negative, most positive value is ground
Array.Sort(floor_heights);
Array.Reverse(floor_heights);
//  ground and first level are one, remove first entry
double[] tmp_arr = new double[floor_heights.Length - 1];
Array.Copy(floor_heights, 1, tmp_arr, 0, floor_heights.Length - 1);

floor_heights = null;
floor_heights = tmp_arr;
...

Étant donné que les coordonnées z ne sont pas dans le bon ordre, le champ est d’abord trié par taille. Cependant, la coordonnée z est dans la direction négative et le tri doit donc toujours être inversé via Inverser. Au départ, il a été déterminé que les fondations et le premier étage comptent pour un seul étage. Le premier élément des hauteurs d’étage est donc supprimé.

Les autres éléments tels que les lignes, les barres et les surfaces sont ensuite transférés :

...
//  get all lines
Console.WriteLine("3. Get all lines:");
line[] lns = new line[line_nums.Length];
for (int i = 0; i < line_nums.Length; ++i)
{
	lns[i] = model.get_line(line_nums[i]);
	if ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");


//  get all members
Console.WriteLine("4. Get all members:");
member[] mems = new member[member_nums.Length];
for (int i = 0; i < member_nums.Length; ++i)
{
	mems[i] = model.get_member(member_nums[i]);
	if ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");

//  get all surfaces
Console.WriteLine("5. Get all surfaces:");
surface[] srfs = new surface[surface_nums.Length];
for (int i = 0; i < surface_nums.Length; ++i)
{
	srfs[i] = model.get_surface(surface_nums[i]);
	if ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");
...

Pour trier les éléments dans les étages individuels, des listes en deux dimensions sont d’abord créées. La liste nds_floor_numbers contient les étages comme première dimension et les numéros de nœuds d’étage comme deuxième dimension. La boucle suivante passe initialement par tous les nœuds et possède une sous-boucle qui parcourt toutes les hauteurs d’étage. Le bâtiment est vérifié de bas en haut afin de déterminer si la coordonnée Z de chaque nœud se trouve dans la hauteur de l’étage. En raison d’imprécisions numériques, une tolérance de 1 mm a été déduite :

...
//  loop through nodes and set their floor
Console.WriteLine("6. Loop through nodes and get their floor");
for (int i = 0; i < nds.Length; ++i)
{
	for (int j = 0; j < floor_heights.Length; ++j)
	{
		if (nds[i].coordinate_3 >= floor_heights[j] - 0.001)
		{
			nds[i].comment = j.ToString();
			//Console.WriteLine("node " + nds[i] + " floor " + j + ";" + nds[i].coordinate_3 + ";" + (floor_heights[j] - 0.001));
			nds_floor_numbers[j].Add(nds[i].no);
			//model.set_node(nds[i]);
			break;
		}
	}
}
...

Dès qu’un nœud se trouve dans la zone spécifiée, le numéro de l’étage est entré sous forme de commentaire et le numéro de nœud est ajouté à la liste. À ce stade, il est également possible de transférer le commentaire dans RFEM (décommenté). Le commentaire est utilisé ici car le numéro de l'étage, et non les coordonnées z, doit être évalué dans le texte suivant.

Cela peut être vu à partir de la boucle au-dessus des lignes :

...
//  loop through lines, get their nodes and set their floor
Console.WriteLine("7. Loop through lines and get their floor");
for (int i = 0; i < lns.Length; ++i)
{
	//  get nodes of line
	int[] ln_node_nums = lns[i].definition_nodes;
	Int32 floor_max = 0;
	// loop through nodes of line
	for (int j = 0; j SMALLERTHAN ln_node_nums.Length; ++j)
	{
		// loop through nodes
		for (int l = 0; l SMALLERTHAN nds.Length; ++l)
		{
			if (nds[l].no == ln_node_nums[j])
			{
				Int32 floor = Int32.Parse(nds[l].comment);
				if (floor > floor_max)
				{
					floor_max = floor;
					break;
				}
			}
		}

	}
	//  enter maxiumum floor in line
	lns[i].comment = floor_max.ToString();
	lns_floor_numbers[floor_max].Add(lns[i].no);
	//model.set_line(lns[i]);

}
...

La boucle passe par toutes les lignes et possède une sous-boucle qui passe par tous les nœuds de la ligne. Dans cette sous-boucle, une autre boucle passe par tous les nœuds pour trouver le nœud correspondant au numéro de nœud à partir de la ligne. Le numéro de l’étage est lu à partir du commentaire de ce nœud.

Comme nous l'avons déjà mentionné dans les bases théoriques, il suffit de déterminer le numéro d’étage le plus élevé. Celui-ci est ensuite enregistré comme numéro d’étage dans le commentaire de la ligne et le numéro de la ligne est inséré dans la liste.

Les barres reçoivent les mêmes numéros d’étage que les lignes sur lesquelles elles reposent. Cela nécessite une boucle sur toutes les barres avec une sous-boucle sur toutes les lignes. Dès que le numéro de ligne correspond à celui de la barre, la hauteur de l’étage de la barre est indiquée sous forme de commentaire et le numéro est ajouté à la liste :

...
// loop through members, get their line and set their floor
Console.WriteLine("8. Loop through members and get their floor");
for (int i = 0; i < mems.Length; ++i)
{
	// get line number of member
	int mem_ln_num = mems[i].line;

	//  loop through lines
	for (int j = 0; j < lns.Length; ++j)
	{
		if (lns[j].no == mem_ln_num)
		{
			mems[i].comment = lns[j].comment;
			mems_floor_numbers[Int32.Parse(lns[j].comment)].Add(mems[i].no);
			break;
		}
	}

}
// loop through surfaces, get their lines and set their floor
Console.WriteLine("9. Loop through surfaces and get their floor");
for (int i = 0; i < srfs.Length; ++i)
{
	//  get lines of surface
	int[] srf_line_nums = srfs[i].boundary_lines;
	Int32 floor_max = 0;
	//  loop through lines of surface
	for (int j = 0; j < srf_line_nums.Length; ++j)
	{
		//  loop through lines
		for (int l = 0; l SMALLERTHAN lns.Length; ++l)
		{
			if (lns[l].no == srf_line_nums[j])
			{
				Int32 floor = Int32.Parse(lns[l].comment);
				if (floor > floor_max)
				{
					floor_max = floor;
					break;
				}
			}
		}

	}
	//  enter maxiumum floor in surface
	srfs[i].comment = floor_max.ToString();
	srfs_floor_numbers[floor_max].Add(srfs[i].no);

	//model.set_surface(srfs[i]);

}
...

La procédure dans les surfaces est la même que dans les lignes. Il y a une boucle sur les surfaces avec une sous-boucle sur leurs lignes de contour. L’étage le plus élevé à partir de la ligne de contour devient le numéro d’étage de la surface.

Après avoir trié tous les éléments dans les étages, les étapes de construction doivent être créées. Une phase de construction est créée pour chaque étage, une boucle est donc créée sur les hauteurs d’étage :

...
Console.WriteLine("10. Set construction stages");
try
{
	model.begin_modification("set construction stages");
	//  create construction stages
	for (int i = 0; i < floor_heights.Length; ++i)
	{
		construction_stage cstg = new construction_stage();

		cstg.no = i + 1;
		cstg.continue_on_construction_stage = i;
		cstg.continue_on_construction_stageSpecified = true;

		cstg.are_members_enabled_to_modify = true;
		cstg.are_members_enabled_to_modifySpecified = true;
		cstg.added_members = mems_floor_numbers[i].ToArray();
		cstg.active_members = cstg.added_members;

		cstg.are_surfaces_enabled_to_modify = true;
		cstg.are_surfaces_enabled_to_modifySpecified = true;
		cstg.added_surfaces = srfs_floor_numbers[i].ToArray();
		cstg.active_surfaces = cstg.added_surfaces;

		cstg.name = "floor " + i;
		cstg.user_defined_name_enabled = true;
		cstg.user_defined_name_enabledSpecified = true;

		model.set_construction_stage(cstg);

	}
}
catch (Exception exception)
{
	model.cancel_modification();
	Console.WriteLine("Something happen when creation of geometry" + exception.Message);
	throw;
}
finally
{
	try
	{
		model.finish_modification();
	}
	catch (Exception exception)
	{
		Console.WriteLine("Something happen when finishing creation of geometry" + exception.Message);
		throw;
	}
}
...

Un bloc begin_modification/finish_modification est également utilisé pour créer des éléments dans RFEM afin d'optimiser la vitesse de transfert. En cas de problème, un bloc try-catch supplémentaire fournit une protection, qui appelle cancel_modification en cas de rupture et termine ainsi le traitement correctement.

Il est important de noter que les éléments à transférer tels que continue_on_construction_stage ne peuvent être transférés que si la propriété « Specified » correspondante a été définie sur « true » (continue_on_construction_stageSpecified). Tous les éléments n’ont pas une telle propriété « Specified », mais lorsqu’elle est disponible, elle doit être prise en compte.

Conclusion

Grâce à la polyvalence de l’interface WebService & API, il est possible d’automatiser manuellement un certain nombre d’entrées complexes. Dans cet exemple, l’entrée du module complémentaire Analyse des phases de construction est automatisée, mais elle sert de paramètre temporaire pour de nombreux autres modules complémentaires et options. Cela signifie qu'il y a peu de limites à la créativité pour trouver des solutions permettant de gagner du temps, et d’autres exemples suivront.


Auteur

M. Günthel fournit une assistance technique aux clients de Dlubal Software.

Téléchargements


;