1022x
001825
2023-08-09

Serviço web e API para a análise das fases de construção

Quando se trata de calcular estruturas regulares, a entrada de dados geralmente não é complicada, mas sim demorada. Entrada automática de dados permite poupar tempo valioso. A tarefa descrita no presente artigo trata de considerar os andares de uma casa como fases individuais de construção. A introdução deveria ser realizada utilizando um programa em C# para que o utilizador não tenha de introduzir manualmente os elementos dos pisos individuais.

Requisitos

O seguinte programa é executado nas seguintes condições:

  • Os planos dos pisos são paralelos ao plano xy.
  • O modelo não pode conter elementos que se extendem por vários pisos (por exemplo, uma superfície de fachada com dois ou mais pisos).
  • A fundação e o rés do chão pertencem ao piso zero.
  • Os sólidos não são considerados.
  • Apenas são utilizadas superfícies planas.
  • O eixo z está direccionado na direção da aceleração da gravidade ("para baixo").

Fundamentação teórica

Com base nos requisitos, fica' claro que apenas a altura do piso deve ser especificada, providenciando um elemento, como um nó, com a sua coordenada z acima da altura inferior do piso e com um máximo igual ou superior a altura de piso relevante.
Portanto, o utilizador tem de selecionar uma série de nós que são representativos para a altura do piso. As coordenadas z e, consequentemente, a altura do piso podem ser determinadas através de um ciclo.
A seguir, os pisos são atribuídos a todos os nós através das alturas.
Para as linhas, é possível recorrer aos pisos dos nós. Se uma linha começa num teto e termina no teto que está por cima, deve ser atribuída ao teto superior. Portanto, a linha é atribuída ao piso mais alto que pode ser encontrado nos seus nós.
Uma vez que as barras estão nas linhas e que podem ser atribuídas previamente, a barra tem o mesmo piso que a sua linha.
O mesmo se aplica às superfícies. É determinado o piso mais alto dos nós correspondentes.
Se todos os elementos de um piso tiverem sido atribuídos, as fases de construção podem ser criadas a partir da lista dos pisos.

Implementação

O programa utiliza como base um modelo do pacote NuGet Dlubal.RFEMWebServiceLibrary. Pode descobrir como instalá-lo aqui:

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

Primeiro, o modelo ativo é ligado da seguinte forma:


            

...
#region Application Settings
try
{
    application_information 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));
...


Para um melhor tratamento de erros, é utilizado um bloco "try-catch" em todo o código-fonte na função principal. Dentro deste bloco, a aplicação é ligada primeiro, o que é feito novamente num blocotry-catch. Após a ligação bem-sucedida da aplicação, o modelo atualmente ativo (em primeiro plano no RFEM 6) é ligado através do método get_active_model. Se ocorrer algum problema, o bloco "try-catch" externo entra em ação.
Uma vez que todos os nós, linhas, barras e superfícies têm de ser analisados, faz sentido obter todos os elementos do RFEM. Para obter o número dos respetivos elementos, são lidos primeiro os números dos objetos de todas as categorias. A seguir, os respetivos objetos podem depois ser transferidos através dos seus 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[] 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 ao utilizador uma visão geral da duração do processo, é escrito um "+" na consola a cada 10 elementos.
Antes de transferir os outros elementos, a altura do piso é determinada no próximo passo. Primeiro, tem de ler os objetos selecionados. A matriz transferida (campo/vetor incluindo elementos) é do tipo local_objeto e contém todos os objetos selecionados com o seu tipo e número. Desta forma, os nós podem ser filtrados num ciclo. Uma vez que todos os nós já estão disponíveis, é possível determinar as coordenadas z desses nós no mesmo ciclo. Segue-se um ciclo através dos objetos selecionados e, assim que um elemento é do tipo de nó, é realizada uma pesquisa entre os nós para este nó e a sua coordenada z é introduzida na matriz andar_alturas. A matriz é aumentada por um 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 = null;
floor_heights = tmp_arr;
...


Uma vez que as coordenadas z não estão na ordem correta, a matriz é primeiro ordenada por tamanho. A coordenada z, no entanto, está na direção negativa e, portanto, a ordenação tem de ser invertida através de Inverter. Inicialmente, foi definido que a fundação e o rés do chão correspondem a um piso, razão pela qual o primeiro elemento na altura dos pisos é removido.
Num próximo passo, os elementos restantes, tais como linhas, barras e superfícies são transferidos:


            

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


Para ordenar os elementos em pisos individuais, são criadas primeiro listas bidimensionais. A lista nds_flow_numbers tem os pisos como primeira dimensão e os números dos nós do piso como segunda dimensão. Em primeiro lugar, o loop seguinte percorre todos os nós e tem um sub-loop que percorre toda a altura dos pisos. Desta forma, é verificado para cada nó a partir da parte inferior no edifício até ao topo se a coordenada Z se encontra dentro da altura do piso. Devido a imprecisões numéricas, foi subtraída uma tolerância 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;
		}
	}
}
...


Assim que um nó estiver dentro da região especificada, o número do piso é introduzido como comentário e o número do nó é adicionado à lista. Neste momento, também seria possível transferir o comentário para o RFEM (comentado). A razão pela qual o comentário é utilizado é porque a seguir não são as coordenadas z que devem ser avaliadas, mas sim o número do piso. Isto é evidente quando observamos o ciclo sobre as linhas:


            

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

	}
	
	lns[i].comment = floor_max.ToString();
	lns_floor_numbers[floor_max].Add(lns[i].no);
	//model.set_line(lns[i]);

}
...


O loop passa por todas as linhas e tem um sub-loop, que passa por todos os nós da linha. Dentro deste sub-loop, existe outro loop a percorrer todos os nós para encontrar o nó que corresponde ao número do nó a partir da linha. Fora deste nó, é lido o número do piso a partir do comentário. Conforme já mencionado nas bases teóricas, basta determinar o número do piso mais alto. Este número é então armazenado como número do piso no comentário da linha e o número da linha é inserido na lista.
As barras recebem os mesmos números de piso das linhas nas quais se encontram. Assim, é necessário um ciclo sobre todas as barras com um sub- ciclo sobre todas as linhas. Assim que o número da linha corresponder ao número da barra, a barra obtém a altura do piso como um comentário e o número é adicionado à 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]);

}
...


O procedimento nas superfícies é idêntico ao das linhas. Existe um ciclo sobre as superfícies com o ciclo parcial sobre as suas linhas de contorno. O piso mais alto a partir da linha de contorno torna-se o número do piso da superfície.
Após ordenar todos os elementos pelos pisos, é necessário criar as fases de construção. É criada uma fase de construção para cada piso, pelo que é feito um loop sobre a altura do piso:


            

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


Für das Anlegen von Elementen in RFEM wird zusätzlich ein begin_modification/finish_modification Block genutzt, um die Übertragungsgeschwindigkeit zu maximieren. Se surgirem problemas, um bloco "try-catch" adicional providencia proteção, o qual chama cancel_modification no caso de uma interrupção, terminando assim o processamento corretamente. Uma nota importante sobre a transferência é que elementos a serem transferidos, tais como continue_on_construction_stage, só podem ser transferidos se a correspondente propriedade "Specified" tiver sido definida como "true" ( continue_on_construction_stageSpecified). Nem todos os elementos têm esta propriedade "especificada", mas deve ser considerada, caso esteja disponível.

Conclusão

Devido à versatilidade da interface Serviço web e API, é possível automatizar um número de entradas que seriam muito demoradas se fossem feitas manualmente. No presente exemplo, a entrada para o módulo Análise das fases de construção é automatizada, o que também serve como marcador de posição para muitos outros módulos e opções. Assim sendo, existem apenas alguns limites para a sua criatividade para soluções que poupam tempo e seguirão-se muitos exemplos.


Autor

O Eng. Günthel opera na área do apoio técnico para clientes.

Downloads