1705x
001773
18.10.2022

Outil de service Web pour l'accumulation d'eau en C#

L'API de RFEM 6, RSTAB 9 et RSECTION est basée sur le concept de services Web. Afin d'obtenir une bonne introduction au sujet, l'article suivant traite d'un autre exemple en C#.

L'exemple représente le calcul d'une charge d'eau sur une poutre, qui augmente en raison de la flèche de la poutre. Pour plus de flexibilité, l'utilisateur doit pouvoir sélectionner la/les poutre(s). De plus, les charges supplémentaires sur la toiture, qui sont appliquées au même cas de charge indépendamment de la charge d'eau, ne doivent pas être modifiées. Les conditions aux limites suivantes sont connues/appliquées :

  • Niveau d'eau au-dessus du système non déformé
  • Largeur d'emprise de la barre pour la charge surfacique
  • Numéro de cas de charge du cas de charge pour lequel la charge doit être appliquée
  • Charge surfacique de l'eau par m du niveau d'eau (10000 N/m³)
  • Identifiant utilisé pour identifier les charges surfaciques

Les variables suivantes sont donc créées dans le programme :

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]

Pour son implémentation, le programme doit contenir les éléments suivants :

  1. Filtrer les barres sélectionnées
  2. Supprimer les charges d'eau des séries précédentes
  3. Créer de nouvelles charges
  4. Lancer le calcul
  5. Déterminer la flèche
  6. Revenir à l'étape 3 et créer de nouvelles charges à partir des déformations
  7. Répéter les itérations jusqu'à ce qu'une limite soit atteinte

En plus de ces fonctions, la connexion au programme et au modèle, divers blocs try-catch et d'autres éléments standard sont nécessaires, mais ils ne seront pas traités plus en détail ici. Ces éléments sont ensuite inclus dans le code source, qui peut être téléchargé ci-dessous.

1. Filtrer les barres sélectionnées

Tout d'abord, les informations sur tous les objets sélectionnés sont obtenues à l'aide de la fonction get_all_selected_objects. Le tableau reçu contient des éléments de type object_location, qui peuvent, par exemple, le type, le numéro et le numéro de l'objet « parent » supérieur. Dans la boucle suivante, les numéros de tous les objets de type E_OBJECT_TYPE_MEMBER (c'est-à-dire tous les numéros de barre) sont extraits et enregistrés dans le tableau mem_noes_sel.

//  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. Supprimer les charges d'eau des séries précédentes

À l'aide des numéros de barre, il est désormais possible de filtrer les charges de barre associées de toutes les charges de barre. Une boucle sur les numéros de charge de barre est utilisée à cet effet. Dans cette boucle, les données pour la charge de barre sont extraites, ses numéros de barre sont vérifiés pour la correspondance avec les numéros de barre sélectionnés et, en cas d'occurrence, la charge de barre correspondante est supprimée :

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

		
	}

}

Afin qu'aucune autre charge de barre ne soit écrasée, le dernier numéro de charge de barre utilisé est lu dans une prochaine étape :

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

L'étape suivante consiste à créer les nouvelles charges de barre, qui font déjà partie de la boucle d'itération do-while. Cette boucle est structurée comme suit :

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

Pour annuler la boucle d'itération, le changement de déformation a été sélectionné. Il est déterminé lors du filtrage des résultats. Si toutes les déformations diffèrent de moins de 0,0001 m (c'est-à-dire 0,1 mm), la boucle est annulée.

3. Créer de nouvelles charges

Une charge trapézoïdale dans la direction z globale est sélectionnée comme type de charge, qui peut avoir des valeurs différentes au début et à la fin de la barre. Il convient de noter que la variable « Spécifiée » associée doit être définie sur « true » pour tous les paramètres à transférer. Ceci est nécessaire pour que tous les paramètres d'une classe ne doivent pas être transmis et que la quantité de données à transmettre puisse être réduite. La valeur de la charge est calculée à partir de la hauteur initiale « h_water » (spécifiée) et de la déformation nodale supplémentaire en ce point. Cette hauteur en [m] est multipliée par « std_magnitude » en [N/m3 ] et la largeur d'alimentation en [m], ce qui donne une charge linéique en [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);
                                        
}

La fonction set_member_load est utilisée pour transférer la charge. Pour pouvoir assigner les charges, la chaîne de caractères water_accu_comment est utilisée comme commentaire. Dans les itérations suivantes, les charges ne sont plus supprimées ; au lieu de cela, en spécifiant le numéro de chargement, elles sont écrasées lorsqu'elles sont transférées à nouveau. Le commentaire n'est donc nécessaire que pour le filtrage et la suppression des charges au redémarrage de l'application. De plus, une référence relative a été choisie pour la spécification de la charge, qui est toutefois comprise entre 0 et 100 %.

L'étape suivante consiste à lancer le calcul. Pour ce faire, un champ est d'abord créé avec des objets de type calculate_specific_loading. Ce champ contient tous les cas de charge/combinaisons de charge à calculer. Dans ce cas particulier, un seul élément du type de cas de charge est créé avec le numéro de cas de charge spécifié 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);

Une fois les résultats disponibles, les déformations globales au début et à la fin de chaque barre doivent être filtrées. La fonction get_results_for_members_global_deformations est utilisée pour obtenir toutes les déformations globales de barre du cas de charge spécifié et des barres sélectionnées. La structure des résultats est la même que dans le tableau correspondant de RFEM 6. Une variante consiste à lire la longueur de barre et à comparer la position x du résultat. Une autre variante utilise la description donnée et le fait que les extrêmes suivent après toutes les positions x. Cette variante a été choisie et lorsque « Extremes » apparaît pour la première fois dans la description, l'index précédent est utilisé pour trouver la dernière position x de la barre. Étant donné que la première position de la barre affecte également la première entrée, aucun autre filtrage n'est nécessaire :

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

Outre la détermination des déformations, la variable « delta_def » est également calculée. Elle est utilisée pour le critère de terminaison. Tout d'abord, la différence entre la valeur de déformation de l'itération précédente (zéro au début) et la valeur actuelle est calculée. La valeur absolue est déduite de la différence, puis le maximum est trouvé. Comme indiqué dans la description de la boucle d'itération, une valeur inférieure à 0,0001 m (c'est-à-dire 0,1 mm) est terminée.

Dans la vidéo ci-jointe, vous pouvez voir le filtre de charge d'une part et l'itération jusqu'à la terminaison d'autre part. À la fin, la charge trouvée est affichée.

L'interface Service Web offre d'innombrables possibilités pour modifier des éléments dans RFEM 6/RSTAB 9, mais également pour lire les résultats. Elle permet de réaliser de nombreux projets différents. Le programme présenté ici comprend une première étape composée de nombreux éléments différents :

  1. Obtenir les éléments sélectionnés
  2. Créer des charges
  3. Obtenir et filtrer les résultats
  4. Filtrer les éléments par commentaire
  5. Supprimer les éléments

Avec cette diversité, le programme est également destiné à servir de modèle type pour d'autres projets.


Auteur

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

Téléchargements


;