1539x
001773
2022-10-18

Herramienta de Servicio web para estancamientos de agua en C#

Las API para RFEM 6, RSTAB 9 y RSECTION están basadas en el concepto de Servicios web. Para obtener una buena introducción al tema, el siguiente artículo explicará un ejemplo en C#.

El ejemplo representa el cálculo de una carga de agua aplicada a una viga, la cual aumenta debido a la flecha de la viga. Para ser más flexible, el usuario debería poder seleccionar la(s) viga(s). Además, no se deberían modificar las cargas adicionales que actúan sobre el techo, las cuales se aplican al mismo caso de carga independientemente de la carga de agua. Se conocen o aplican las siguientes condiciones de contorno:

  • Nivel del agua por encima del sistema no deformado
  • Anchura de la zona de influencia de la barra para la carga superficial
  • Número de caso de carga para el que se va a aplicar la carga
  • Carga superficial de agua por m de nivel de agua (10.000 N/m³)
  • Un identificador utilizado para identificar las cargas superficiales

Por lo tanto, se crean las siguientes variables en el programa:


            

double h_water = 0.1;// altura del agua sobre el sistema no deformado en [m]
double w_ref = 2; // ancho de referencia para la carga superficial en [m]
int load_case_no = 1; // número del caso de carga donde se aplica la carga
string water_accu_comment = "acumulación de agua"; // cadena de identificación
double std_magnitude = 10000; // carga superficial por altura de agua en [N/m^3]


Con respecto a la implementación, el programa debe incluir los siguientes elementos:

  1. Filtrar las barras seleccionadas
  2. Eliminar cargas de agua de ejecuciones anteriores
  3. Creación de nuevas cargas
  4. Iniciar cálculo
  5. Determinación de la flecha
  6. Volver al paso 3 y crear nuevas cargas a partir de las deformaciones
  7. Repetición de iteraciones hasta que se alcanza un valor límite

Además de estas funciones, se requiere la conexión al programa y al modelo, varios bloques try-catch y otros elementos estándar, pero no se describen aquí con más detalle. Estos elementos se incluyen en el código fuente, los cuales se puede descargar debajo del artículo.

1. Filtrar barras seleccionadas

Primero, obtenemos información sobre todos los objetos seleccionados usando la función get_all_selected_objects. La matriz obtenida contiene elementos del tipo object_location, que contienen, por ejemplo, el tipo, el número y el número de objeto "principal" superior. En la siguiente iteración, se extraen los números de todos los objetos del tipo E_OBJECT_TYPE_MEMBER (es decir, todos los números de barras) y se guardan en la matriz mem_noes_sel.


            

//obtener los objetos seleccionados
object_location[] obj_locs = model.get_all_selected_objects();

//extraer barras
int[] mem_noes_sel = new int[0];
foreach (object_location obj_loc en 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. Eliminar cargas de agua de ejecuciones anteriores

Por medio de los números de barra, ahora es posible filtrar las cargas de barra asociadas de todas las cargas en barra. Se usa una iteración que se coloca sobre los números de carga en las barras. Dentro de esta iteración, obtenemos los datos para la carga en la barra, verificamos que sus números de barra estén de acuerdo con los números de barra seleccionados y, si hay una coincidencia, se elimina la carga en barra correspondiente:


            

//eliminar todas las cargas water_accu
//obtener todos los números de carga de barras
int[] mem_load_noes = model.get_all_object_numbers(object_types.E_OBJECT_TYPE_MEMBER_LOAD, load_case_no);

// iteración por todas las cargas en barras del caso de carga
foreach(int mem_load_no in mem_load_noes)
{
	// obtener carga en barra
	member_load mem_load = model.get_member_load(mem_load_no, load_case_no);

	if(mem_load.comment == water_accu_comment)
	{
		// iteración a través de las barras de la carga en barra
		for(int i= 0; i < mem_load.members.Length; i++)
		{
			// iteración a través de las barras de la carga en barra
			for (int j = 0; j < mem_noes_sel.Length; j++)
			{
				if(mem_load.members[i] == mem_noes_sel[j])
				{
					// eliminar la carga en barra
					model.delete_object(object_types.E_OBJECT_TYPE_MEMBER_LOAD, mem_load_no, load_case_no);
				}
			}
		}

		
	}

}


Para que no se sobrescriban otras cargas en barra, en un paso siguiente se lee el último número de la carga en barra usada:


            

// obtener el último número de carga
int no_offset = model.get_nth_object_number(object_types.E_OBJECT_TYPE_MEMBER_LOAD,0, load_case_no) + 1;


Ahora, el siguiente paso es crear las nuevas cargas en barra, que ya son parte del ciclo de iteración do-while. Esta iteración se construye de la siguiente manera:


            

do
{
	// restablecer la deformación delta
	delta_def = 0;
	
	// aplicar carga
	model.begin_modification ("Cargas");
	
    // crea cargas en barra para cada barra
	...
    
    model.finish_modification ();
    ...
    
	// calcular el caso de carga
	...


	// obtener deformaciones en los extremos de barra
	...

// comprobar criterio
} while (delta_def > 0.0001);


Para detener el bucle de iteración, se seleccionó el cambio de la deformación, el cual se determina al filtrar los resultados. Si todas las deformaciones se desvían de la deformación en el tramo anterior en menos de 0,0001 m (es decir, 0,1 mm), el bucle se detiene.

3. Creación de nuevas cargas

Se selecciona una carga trapezoidal que actúa en la dirección z global como tipo de carga, que puede tener valores diferentes al inicio y al final de la barra. Tenga en cuenta que la variable "Specified" (especificada) asociada se debe establecer en "true" (verdadero) para que se transfieran todos los parámetros. Esto es necesario para no transferir todos los parámetros de una clase y para mantener la cantidad de datos a transferir bastante baja. El valor de la carga se calcula a partir de la altura inicial "h_water" (se especificó) y la deformación en nudo adicional en esta posición. La altura dada en [m] se multiplica por "std_magnitude" en [N/m3 ] y el ancho de la zona de influencia dado en [m], resultando en una carga lineal dada en [N/m]:


            

// crea cargas en barra para cada barra
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 función set_member_load se usa para transferir la carga. Para asignar las cargas, se usa la string (cadena de caracteres) water_accu_comment como comentario. En las siguientes iteraciones, las cargas ya no se eliminan; en su lugar, al especificar el número de carga, se sobrescriben cuando se transfieren de nuevo. Por lo tanto, el comentario solo es necesario para filtrar y eliminar las cargas cuando se reinicia la aplicación. Además, se eligió una referencia relativa para la especificación de la carga, que, sin embargo, oscila entre 0 y 100%.

El siguiente paso es iniciar el cálculo. Primero, se crea un campo con objetos del tipo calculate_specific_loading. Este campo contiene todos los casos y combinaciones de carga a calcular. En el caso presente, sólo se crea un elemento del tipo de caso de carga con el número de caso de carga especificado load_case_no:


            

// calcular el caso de carga
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);


Ahora que los resultados están disponibles, se deben filtrar las deformaciones globales al principio y al final de cada barra. La función get_results_for_members_global_deformations se usa para obtener todas las deformaciones globales de las barras del caso de carga especificado y las barras seleccionadas. La estructura de los resultados es la misma que en la tabla correspondiente en RFEM 6. Una variante es leer la longitud de la barra y comparar la posición x del resultado. Otra variante usa la descripción dada y el hecho de que los extremos siguen después de todas las posiciones en x. Se eligió la segunda variante, y cuando "Extremes" (extremos) aparece en la descripción por primera vez, se usa el índice anterior para encontrar la última posición x de la barra. Dado que la primera posición de la barra también afecta a la primera entrada, no es necesario realizar más filtros aquí:


            

// obtener deformaciones en los extremos de barra
para (int i = 0; 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]);

    // tomar el punto de inicio
    // calcular la deformación delta
    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;
    
    // obtener deformación en el punto final
    for (int j = 0; j < mem_defs_glbl.Length; ++j)
    {
        if (mem_defs_glbl[j].description == "Extremes")
        {
            // calcular la deformación delta
            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;
        }
    }
}


Además de determinar las deformaciones, también se calcula la variable "delta_def", que se utiliza para el criterio de interrupción. Primero, se calcula la diferencia entre el valor de deformación de la iteración anterior (cero al principio) y el valor actual. El valor absoluto se toma de la diferencia y luego se encuentra el máximo. Como ya se muestra en la descripción del bucle de iteración, nos detenemos en un valor menor a 0,0001 m (es decir, 0,1 mm).

En el vídeo adjunto a este artículo, puede ver por un lado el filtro de carga y por otro los pasos de iteración hasta la interrupción. Al final, se muestra la carga encontrada.

La interfaz del servicio web ofrece innumerables opciones para modificar elementos en RFEM 6/RSTAB 9, pero también para leer los resultados. Con esta se pueden realizar muchos proyectos diferentes. El programa que se muestra aquí en este artículo incluye solo un primer paso de muchos elementos diferentes:

  1. Obtener elementos seleccionados
  2. Crear cargas
  3. Obtener y filtrar resultados
  4. Filtrar elementos por comentario
  5. Eliminar elementos

Con esta variedad, el programa también está destinado a servir como modelo para otros proyectos.


Autor

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

Descargas