912x
001825
2023-08-09

Servicio web y API para el análisis de las fases de construcción

Al calcular estructuras regulares, la entrada de datos a menudo no es complicada pero requiere mucho tiempo. La automatización de la entrada de datos puede ahorrar un tiempo valioso. La tarea descrita en el presente artículo es considerar las plantas de una casa como etapas de construcción individuales. Los datos se introducen utilizando un programa C# para que el usuario no tenga que introducir los elementos de las plantas individuales manualmente.

Requisitos

El programa siguiente se ejecuta bajo estas condiciones:

  • Los planos de la planta son paralelos al plano xy.
  • El modelo no debe contener ningún elemento que abarque varias plantas (por ejemplo, una superficie de fachada que abarque dos o más plantas).
  • La cimentación y el primer piso pertenecen a la planta cero.
  • No se consideran sólidos.
  • Solo se utilizan superficies planas.
  • El eje z está dirigido en la dirección de la aceleración de la gravedad ("hacia abajo").

Base teórica

Basándose en los requisitos, está claro que solo se deben especificar las alturas de las plantas, proporcionando un elemento, como un nudo, con su coordenada z por encima de la altura de la planta inferior y hasta un máximo de o igual a la altura de la planta relevante.
Por lo tanto, el usuario tiene que seleccionar una serie de nudos que sean representativos para las alturas de las plantas. Las coordenadas en z, junto con las alturas de las plantas, se pueden determinar por medio de un bucle (loop).
A continuación, las plantas se asignan a todos los nudos utilizando las alturas.
Para las líneas, es posible retroceder a los plantas de los nudos. Si una línea comienza en un techo y termina en el techo que se encuentra encima, se debe asignar al techo superior. Por lo tanto, la línea se asigna a la planta más alta que se puede encontrar en sus nudos.
Dado que las barras se encuentran en líneas, y dado que se pueden asignar antes, la barra tiene la misma planta que su línea.
Lo mismo se aplica a las superficies. Se determina la planta más alta de los nudos correspondientes.
Si se han asignado todos los elementos de una planta, se pueden crear las fases de construcción a partir de las listas de las plantas.

Aplicación

El programa utiliza una plantilla del paquete Dlubal.RFEMWebServiceLibrary NuGet como base. Aquí puede encontrar cómo instalarla:

Primero, el modelo activo se conecta de la siguiente manera:


            

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

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

            aplicacion = 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));
...


Para un mejor manejo de errores, se usa un bloque try-catch en todo el código fuente en la función principal. Dentro de este bloque, la aplicación se conecta primero, lo que se hace de nuevo en un bloque try-catch. Después de conectar con éxito la aplicación, el modelo activo actualmente (en primer plano de RFEM 6) se conecta mediante el método get_active_model. Si se produce algún problema, toma efecto el bloque try-catch exterior.
Dado que se deben analizar todos los nudos, líneas, barras y superficies, tiene sentido obtener todos los elementos de RFEM. Para obtener el número de los elementos respectivos, primero se leen los números de objeto de todas las categorías. A continuación, los objetos respectivos se pueden transferir por medio de sus números:


            

...
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("");
...


Para dar al usuario una visión general de la duración del proceso, se escribe un "+" en la consola cada 10 elementos.
Antes de transferir otros elementos, las alturas de las plantas se determinan en el siguiente paso. Primero, tiene que leer los objetos seleccionados. La matriz transferida (campo/vector incluyendo elementos) es del tipo object_location y contiene todos los objetos seleccionados con su tipo y número. De esta manera, los nudos se pueden filtrar en un bucle. Dado que todos los nudos ya están disponibles, es posible determinar las coordenadas en z de estos nudos en el mismo bucle. Hay un bucle que recorre los objetos seleccionados, y tan pronto como un elemento sea del tipo de nudo, se ejecuta una búsqueda entre los nudos para este nudo, y luego se introduce su coordenada z en la matriz Floor_heights. La matriz se amplía con un elemento para cada coordenada encontrada:


            

...
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 = zero;
floor_heights = tmp_arr;
...


Dado que las coordenadas en z no están en el orden correcto, la matriz (array) se ordena primero por tamaño. Sin embargo, la coordenada z está en la dirección negativa y, por lo tanto, la clasificación se debe invertir mediante Reverse. Inicialmente, se definió que la cimentación y el primer piso cuentan como una planta, por lo que se elimina el primer elemento en las alturas del piso.
En el siguiente paso, se transfieren los elementos restantes, como líneas, barras y superficies:


            

...
//  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]);
	si ((i % 10) == 0)
		Console.Write("+");
}
Console.WriteLine("");
...


Para clasificar los elementos en plantas individuales, primero se crean listas bidimensionales. La lista nds_floor_numbers tiene las plantas como una primera dimensión y los números de los nudos de la planta como la segunda dimensión. En primer lugar, el siguiente bucle pasa por todos los nudos y tiene un subbucle, que pasa por todas las alturas de la planta. De esta forma, se comprueba para cada nudo desde la parte inferior del edificio hasta la parte superior si la coordenada Z se encuentra dentro de la altura de la planta. Debido a imprecisiones numéricas, se restó una tolerancia de 1 mm:


            

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


Tan pronto como un nudo se encuentre dentro de la región especificada, el número de la planta se introduce como un comentario y el número del nudo se agrega a la lista. En este punto, también sería posible transferir el comentario a RFEM (está comentado). La razón por la que se usa el comentario es porque a continuación no se van a evaluar las coordenadas en z, sino el número de la planta. Esto es evidente cuando se mira el bucle sobre las líneas:


            

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

}
...


El bucle pasa por todas las líneas y tiene un subbucle, que pasa por todos los nudos de la línea. Dentro de este subbucle, hay otro bucle que recorre todos los nudos para encontrar el nudo que coincida con el número de nudo de la línea. A partir de este nudo, se lee el número de la planta del comentario. Como ya se mencionó en los conceptos básicos teóricos, es suficiente determinar el número de la planta más alta. Este número se almacena como el número de la planta en el comentario de la línea, y el número de la línea se inserta en la lista.
Las barras obtienen los mismos números de planta que las líneas en las que se encuentran. Por lo tanto, se requiere un bucle sobre todas las barras con un subbucle sobre todas las líneas. Tan pronto como el número de línea coincida con el número de la barra, la barra obtiene su altura de planta como un comentario y el número se agrega a la lista:


            

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

}
...


El procedimiento en superficies es similar al de las líneas. Hay un bucle sobre las superficies con el subbucle sobre sus líneas de contorno. La planta más alta desde la línea de contorno se convierte en el número de planta de la superficie.
Después de clasificar todos los elementos en las plantas, se deben crear las fases de construcción. Se crea una fase de construcción para cada planta, por lo que se realiza un bucle sobre las alturas de las plantas:


            

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


Para crear elementos en RFEM, se usa adicionalmente un bloque begin_modification/finish_modification para aumentar la velocidad de transferencia. Si surgen problemas, un bloque try-catch adicional proporciona protección, que llama a cancel_modification en caso de una interrupción, finalizando así el procesamiento correctamente. Una nota importante sobre la transferencia es que los elementos a transferir como continue_on_construction_stage solo se pueden transferir si la propiedad "Specified" correspondiente se ha establecido en "true" (continue_on_construction_stageSpecified). No todos los elementos tienen una propiedad "Specified", pero donde esté disponible, se debe tener en cuenta.

Resumen

Gracias a la versatilidad de la interfaz del Servicio web y API, es posible automatizar una serie de entrada de datos que llevarían mucho tiempo si se hicieran manualmente. En el ejemplo actual, la entrada para el complemento Análisis de fases de construcción está automatizada, lo cual también sirve como modelo a seguir para muchos otros complementos y opciones. Así pues, solo hay unos pocos límites para su creatividad para soluciones que ahorren tiempo, y seguirán más ejemplos.


Autor

El Sr. Günthel proporciona soporte técnico para los clientes de Dlubal Software y se ocupa de sus solicitudes.

Descargas