1599x
001773
2022-10-18

Ferramenta de serviço web para a formação de bolsas de água em C#

A API para o RFEM 6, o RSTAB 9 e o RSECTION baseia-se no conceito de serviços web. Para obter uma boa introdução ao assunto, o artigo seguinte explica um exemplo adicional em C#.

O exemplo representa o cálculo de uma carga de água aplicada a uma viga, que aumenta devido à flecha da viga. Para ser mais flexível, o utilizador deve poder selecionar as vigas. Além disso, as cargas adicionais que atuam na cobertura, que são aplicadas no mesmo caso de carga independentemente da carga de água, não deveriam ser alteradas. As seguintes condições de fronteira são conhecidas/aplicadas:

  • Nível de água acima do sistema não deformado
  • Largura da zona de avanço da barra para carga superficial
  • Número do caso de carga do caso de carga para o qual a carga deve ser aplicada
  • Carga de superfície de água por m de nível de água (10000 N/m³)
  • Um identificador utilizado para identificar as cargas de superfície

Por isso, são criadas as seguintes variáveis no programa:


            

double h_água = 0,1;//altura da água sobre um sistema não deformado em [m]
double w_ref = 2;//largura de referência para carga de superfície em [m]
int não_caso_carga = 1;//número do caso de carga onde a carga é aplicada
string water_accu_comment = "acumulação de água";//string de identificação
double magnitude_padrão = 10000;//carga de superfície por altura de água em [N/m^3]


No que diz respeito à implementação, o programa deve incluir os seguintes elementos:

  1. Filtrar barras selecionadas
  2. Eliminar cargas de água de execuções anteriores
  3. Criar novas cargas
  4. Iniciar o cálculo
  5. Determinação da flecha
  6. Voltar ao passo 3 e criar novas cargas a partir de deformações
  7. Repetir iterações até ser alcançado um valor limite

Além destas funções, são necessárias a ligação ao programa e modelo, vários blocos try-catch e outros elementos padrão, mas não são descritos em maior detalhe aqui. Estes elementos serão depois contidos no código-fonte, que pode ser descarregado abaixo do artigo.

1. Filtrar barras selecionadas

Primeiro, obtemos informação sobre todos os objetos selecionados utilizando a função get_all_selected_objects. A matriz obtida contém elementos do tipo localização_objeto, que contêm, por exemplo, o tipo, o número e o número do objeto "pai" superordenado. No loop seguinte, os números de todos os objetos do tipo E_OBJECT_TYPE_MEMBER (isto é, todos os números de barra) são extraídos e salvos no array mem_noes_sel.


            

//pega os objetos selecionados
object_location[] obj_locs = model.get_all_selected_objects();

//extrai barras
int[] mem_noes_sel = new int[0];
foreach(local_objeto obj_loc in obj_locs)
{
	if(obj_loc.type == object_types.E_OBJECT_TYPE_MEMBER)
	{
		Array.Resize(ref mem_noes_, mem_noes_sel.Length + 1);
		mem_noes_sel[mem_noes_sel.Length-1] = obj_loc.no;
	}
}


2. Eliminar cargas de água de execuções anteriores

Através dos números da barra, agora é possível filtrar as cargas de barra associadas de todas as cargas de barra. É utilizado um anel sobre os números da carga de barra. Neste ciclo, obtemos os dados para a carga de barra, verificamos se os números de barra estão de acordo com os números de barra selecionados e, se houver uma correspondência, a carga de barra correspondente é eliminada:


            

//elimina todas as cargas de água_accu
//obtém todos os números de cargas de barra
int[] mem_load_noes = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_MEMBER_LOAD, load_case_no);

//percorre todas as cargas de barra do caso de carga
foreach(int mem_load_no in mem_load_noes)
{
	//obtém a carga de barra
	member_load mem_load = model.get_member_load(mem_load_no, load_case_no);

	if(mem_load.comment == water_accu_comment)
	{
		//percorre as barras do carregamento de barra
		for(int i= 0; i < mem_load.members.Length; i++)
		{
			//percorre as barras do carregamento de barra
			for (int j = 0; j < mem_noes_sel.Length; j++)
			{
				if(mem_load.members[i] == mem_noes_sel[j])
				{
					//elimina a carga de barra
					model.delete_object(object_types.E_OBJECT_TYPE_MEMBER_LOAD, mem_load_no, load_case_no);
				}
			}
		}

		
	}

}


Para que nenhuma outra carga de barra seja substituída, no passo seguinte, o último número de carga de barra utilizado é lido:


            

//obtém o último número de carga
int desvio_nº = modelo.nth_núm_objeto(tipos_objeto.E_OBJECT_TYPE_MEMBER_LOAD,0, nº_caso_carga) + 1;


Agora, o próximo passo é criar as novas cargas de barra, que já faz parte do ciclo de iteração do-while. Este ciclo é construído da seguinte forma:


            

do
{
	//reinicia a deformação delta
	definição_delta = 0;
	
	//aplica a carga
	model.begin_modification("Cargas");
	
    //cria cargas de barra para cada barra
	...
    
    modelo.modificação_de_acabamento();
    ...
    
	//calcula o caso de carga
	...


	//obtém as deformações da extremidade da barra
	...

//verifica o critério
} while (def_delta > 0,0001);


Para parar o ciclo de iteração, foi selecionada a alteração da deformação, que é determinada ao filtrar os resultados. Se todas as deformações diferirem menos de 0,0001 m (isto é, 0,1 mm), em relação à deformação anterior, o laço é interrompido.

3. Criar novas cargas

Uma carga trapezoidal que atua na direção z global é selecionada como tipo de carga, a qual pode ter valores diferentes no início e no final da barra. Observe que a variável "Specified" associada deve ser definida como "true" para que todos os parâmetros sejam transferidos. Isto é necessário para não transferir todos os parâmetros de uma classe e para manter a quantidade de dados a serem transferida bastante baixa. O valor da carga's é calculado a partir da altura inicial "h_water" (foi especificado) e da deformação nodal adicional neste local. A altura dada em [m] é multiplicada por "std_magnitude" em [N/m3 ] e a largura da zona de alimentação é dada em [m], resultando numa carga de linha em [N/m]:


            

//cria cargas de barra para cada barra
for (int i = 0; i < mem_noes_sel.Length; ++i)
{
	carga_mem_carga = new carga_membro();
	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 = verdadeiro;
	mem_load.distance_a_is_defined_as_relativeSpecified = verdadeiro;
	mem_load.distância_a_relativa = 0,0;
	mem_load.distance_a_relativeSpecified = verdadeiro;

	mem_load.distance_b_is_defined_as_relative = verdadeiro;
	mem_load.distance_b_is_defined_as_relativeSpecified = verdadeiro;
	mem_load.distance_b_relative = 1,0;
	mem_load.distance_b_relativeSpecified = verdadeiro;

	mem_load.direção_de_carga = direção_de_carga_carga_barra.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_distribution.LOAD_DISTRIBUTION_TRAPEZOIDAL;
	mem_load.load_distributionSpecified = true;
	
    mem_load.magnitude_1 = magnitude_std * ((h_water + mem_fim_defs[i,0]) * w_ref);
	mem_load.magnitude_1Especificado = verdadeiro;
	mem_load.magnitude_2 = magnitude_std * ((h_water + mem_fim_defs[i,1]) * w_ref);
	mem_load.magnitude_2Especificado = verdadeiro;

	model.set_member_load(no_caso_carga, carga_mem);
    
}


A função set_member_load é utilizada para transferir a carga. Para atribuir as cargas, é utilizado o string water_accu_comment como comentário. Nas iterações seguintes, as cargas não são mais eliminadas; em vez disso, pela especificação do número da carga, estas são substituídas quando são transferidas novamente. O comentário só é necessário para filtrar e eliminar as cargas quando a aplicação é reiniciada. Além disso, foi escolhida uma referência relativa para especificar a carga, que, no entanto, varia entre 0-100%.

O passo seguinte é iniciar o cálculo. Em primeiro lugar, é criado um campo com objetos do tipo calcular_específico_carregamento. Este campo contém todos os casos/combinações de carga a serem calculados. No presente caso, apenas é criado um elemento do tipo de caso de carga com o número do caso de carga especificado load_case_no:


            

//calcula o caso de carga
calcular_especifico_carga[] csl = new calcular_especifico_carregamento[1];
csl[0] = new calcular_específico_carregamento();
csl[0].no = load_case_no;
csl[0].type = case_object_types.E_OBJECT_TYPE_LOAD_CASE;

model.calcular_specific(csl, verdadeiro);


Agora que os resultados estão disponíveis, é necessário filtrar as deformações globais no início e no final de cada barra. A função get_results_for_members_global_deformations é utilizada para obter todas as deformações globais de barra do caso de carga especificado e das barras selecionadas. A estrutura dos resultados é a mesma da tabela correspondente no RFEM 6. Uma variante é ler o comprimento da barra e comparar a posição x do resultado. Uma outra variante utiliza a descrição dada e o facto de os extremos se seguirem a todas as posições x. A segunda variante foi escolhida e, quando "Extremos" aparece pela primeira vez na descrição, o índice anterior é utilizado para encontrar a última posição x da barra. Uma vez que a primeira posição da barra também afeta a primeira entrada, não é necessária mais nenhuma filtragem aqui:


            

//obtém as deformações da extremidade da barra
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]);

    //pega ponto inicial
    //calcula a deformação delta
    if (Math.Abs(mem_fim_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;
    
    //obtém a deformação no ponto final
    for (int j = 0; j < mem_defs_glbl.Length; ++j)
    {
        if (mem_defs_glbl[j].description == "Extremos")
        {
            //calcula a deformação delta
            if (Math.Abs(mem_fim_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_fim_defs[i, 1] = mem_defs_glbl[j - 1].row.displacement_z;
            pausa;
        }
    }
}


Além de determinar as deformações, também é calculada a variável "delta_def", a qual é utilizada como o critério de paragem. Em primeiro lugar, é calculada a diferença entre o valor da deformação da iteração anterior (zero no início) e o valor atual. O valor absoluto é retirado da diferença e, em seguida, o máximo é encontrado. Como já foi apresentado na descrição do ciclo de iteração, paramos num valor inferior a 0,0001 m (ou seja, 0,1 mm).

No vídeo anexado a este artigo, é possível ver o filtro de carga, por um lado, e os passos da iteração até ao limite, por outro. No final, é apresentada a carga encontrada.

A interface do serviço web oferece inúmeras opções para modificar elementos no RFEM 6/RSTAB 9, mas também para ler os resultados. Com isso, podem ser implementados muitos projetos diferentes. O programa apresentado neste artigo inclui apenas a primeira etapa de muitos elementos diferentes:

  1. Obter elementos selecionados
  2. Criar cargas
  3. Obter e filtrar resultados
  4. Filtrar elementos por comentário
  5. Eliminar elementos

Devido a esta variedade, o programa pretende também servir de modelo para outros projetos.


Autor

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

Downloads