Наш пример представляет собой расчет нагрузки от воды, приложенной к балке, которая увеличивается из-за прогиба балки. Для удобства пользователь должен иметь возможность выбрать балку или балки. Кроме того, не должны изменяться дополнительные нагрузки на кровлю, которые действуют в одном и том же загружении независимо от нагрузки от воды. Известны/применены следующие граничные условия:
- Уровень воды над недеформированной системой
- Ширина зоны подачи стержня для распределенной по площади нагрузки
- Номер загружения, в котором должна быть приложена нагрузка
- Нагрузка от воды на м уровня воды (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]
Что касается применения, то программа должна включать следующие элементы:
- Фильтрация выбранных стержней
- Удаление нагрузок от воды из предыдущих прогонов
- Создание новых нагрузок
- Начать расчет
- Определение прогиба
- Возврат к шагу 3 и создание новых нагрузок от деформаций
- Повторение итераций до тех пор, пока не будет достигнуто предельное значение
Кроме этих функций, требуется соединение с программой и моделью, использование различных блоков 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, а также для считывания результатов. С его помощью можно реализовать множество различных проектов. Программа, представленная в нашей статье, включает в себя только первый шаг из множества различных элементов:
- Выбор обозначенных элементов
- Создание нагрузок
- Получение и фильтрация результатов
- Фильтрация элементов по комментариям
- Удаление элементов
Благодаря такому разнообразию, программа может служить шаблоном для других проектов.