1823x
001773
2022-10-18

Инструмент веб-сервис для подпорной воды в C#

API для программ RFEM 6, RSTAB 9 и RSECTION основан на концепции веб-сервисов. Для более подробного представления о данном предмете в нашей статье будет показан еще один пример в C#.

Наш пример представляет собой расчет нагрузки от воды, приложенной к балке, которая увеличивается из-за прогиба балки. Для удобства пользователь должен иметь возможность выбрать балку или балки. Кроме того, не должны изменяться дополнительные нагрузки на кровлю, которые действуют в одном и том же загружении независимо от нагрузки от воды. Известны/применены следующие граничные условия:

  • Уровень воды над недеформированной системой
  • Ширина зоны подачи стержня для распределенной по площади нагрузки
  • Номер загружения, в котором должна быть приложена нагрузка
  • Нагрузка от воды на м уровня воды (10000 Н/м³)
  • Идентификатор, используемый для идентификации распределенных нагрузок

Поэтому в программе создаются следующие переменные:

double h_water = 0.1;   //  высота воды над недеформированной системой в [м]
double w_ref = 2;       //  исходная ширина при нагрузке на поверхность в [м]
iint load_case_no = 1;   //  номер загружения, в котором приложена нагрузка
string water_accu_comment = "water accumulation";  //  строка идентификатора
double std_magnitude = 10000;   //  нагрузка на поверхность на высоту воды в [Н/м^3]

Что касается применения, то программа должна включать следующие элементы:

  1. Фильтрация выбранных стержней
  2. Удаление нагрузок от воды из предыдущих прогонов
  3. Создание новых нагрузок
  4. Начать расчет
  5. Определение прогиба
  6. Возврат к шагу 3 и создание новых нагрузок от деформаций
  7. Повторение итераций до тех пор, пока не будет достигнуто предельное значение

Кроме этих функций, требуется соединение с программой и моделью, использование различных блоков try-catch и другие стандартные элементы, которые мы не будем более подробно описывать. Эти элементы затем включены в исходный код, который можно скачать по ссылке в нашей статье.

1. Фильтрация выбранных стержней

Сначала мы получим информацию о всех выбранных объектах с помощью функции get_all_selected_objects. Полученный массив содержит элементы object_location, которыми могут быть, например, тип, номер и «материнский» номер объекта более высокого порядка. В следующем цикле извлекаются номера всех объектов типа E_OBJECT_TYPE_MEMBER (т.е. все номера стержней) и сохраняются в массиве 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. Удаление нагрузок от воды из предыдущих прогонов

С помощью номера стержня теперь можно отфильтровать соответствующие нагрузки на стержень из всех нагрузок на стержни. При этом используется цикл, наложенный на номера нагрузок на стержень. В данном цикле мы получаем данные для нагрузки на стержень, проверяем номера стержней на соответствие выбранным номерам стержней и, если найдено совпадение, соответствующая нагрузка на стержень удаляется:

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

		
	}

}

Для того, чтобы никакие другие нагрузки на стержень не были переписаны, считывается последний использованный номер нагрузки на стержень:

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

Следующим шагом будет создание новых нагрузок на стержень, которое уже является частью итерационного цикла do-while. Данный цикл имеет следующую структуру:

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

Для остановки итерационного цикла было выбрано изменение деформации, определенное при фильтрации результатов. Если все деформации отклоняются от деформации предыдущего прохода менее чем на 0,0001 м (т.е. 0,1 мм), то цикл останавливается.

3. Создание новых нагрузок

В качестве типа нагрузки была выбрана трапециевидная нагрузка, действующая в глобальном направлении z, которая может иметь различные значения в начале и на конце стержня. Обратите внимание также на то, что для передачи всех параметров соответствующая переменная «Specified» должна быть установлена на «true». Это необходимо для того, чтобы не передавать все параметры класса и таким образом сохранить относительно небольшой объем данных, которые должны быть переданы. Значение нагрузки рассчитывается на основе начальной высоты «h_water» (была задана) и дополнительной узловой деформации в данной точке. Затем нужно умножить высоту в [м] на «std_magnitude» в [Н/м3] и на ширину зоны нагрузки, указанную в [м], в результате мы получим линейную нагрузку в [Н/м]:

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

Функция set_member_load используется для передачи нагрузки. Для присвоения нагрузок используется строка water_accu_comment в качестве комментария. В следующих итерациях нагрузки уже не удаляются, вместо этого, при указании номера нагрузки, они будут заменены при повторной передаче. Таким образом, комментарий требуется только для фильтрации и удаления нагрузок при перезапуске приложения. Кроме того, была выбрана относительная величина для задания нагрузки, которая колеблется в диапазоне 0-100%.

Следующим шагом будет инициирование самого расчета. Сначала создается поле с объектами типа calculate_specific_loading. Это поле содержит все рассчитываемые загружения/сочетания. В данном случае создается только один элемент типа загружения с указанным номером загружения 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);

Теперь, когда получены результаты, необходимо отфильтровать общие деформации в начале и на конце каждого стержня. Функция get_results_for_members_global_deformations используется для получения всех общих деформаций стержней для указанного загружения и выбранных стержней. Структура результатов такая же, как в соответствующей таблице в RFEM 6. Одним из вариантов является считывание длины стержня и сравнение результата с позицией x. Другой вариант - использовать данное описание (description) и тот факт, что экстремумы следуют после всех локаций x. Был выбран второй вариант, и при первом упоминании «экстремумы» в описании используется предыдущий индекс для нахождения последней локации х стержня. Поскольку первое местоположение стержня также влияет на первую запись, дальнейшая фильтрация здесь не требуется:

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

Кроме вычисления деформаций, рассчитывается переменная «delta_def», которая используется для критерия остановки цикла. Сначала рассчитывается разница между значением деформации предыдущей итерации (ноль в начале) и текущим значением. Из разности берется абсолютное значение и затем находится максимум. Как уже было показано в описании итерационного цикла, цикл остановлен при значении меньшем, чем 0,0001 м (т.е. 0,1 мм).

В видео, прилагаемом к данной статье, можно увидеть, с одной стороны, фильтр нагрузок, а с другой, шаги итерации до остановки. В конце отображается найденная нагрузка.

Интерфейс веб-сервис содержит множество возможностей для изменения элементов в RFEM 6/RSTAB 9, а также для считывания результатов. С его помощью можно реализовать множество различных проектов. Программа, представленная в нашей статье, включает в себя только первый шаг из множества различных элементов:

  1. Выбор обозначенных элементов
  2. Создание нагрузок
  3. Получение и фильтрация результатов
  4. Фильтрация элементов по комментариям
  5. Удаление элементов

Благодаря такому разнообразию, программа может служить шаблоном для других проектов.


Автор

Г-н Гюнтель осуществляет техническую поддержку пользователей Dlubal Software.

Скачивания


;