Текущий раздел:

Описания RDS

Описание пользователя

Глава 3. Использование стандартных модулей автокомпиляции

§3.6. Принципы создания автокомпилируемых моделей блоков

§3.6.4. Моделирование длящихся во времени процессов

§3.6.4.4. Создание динамического блока по шаблону

# Описание пользователя

## Глава 3. Использование стандартных модулей автокомпиляции

### §3.6. Принципы создания автокомпилируемых моделей блоков

#### §3.6.4. Моделирование длящихся во времени процессов

##### §3.6.4.4. Создание динамического блока по шаблону

Описывается создание динамического блока при помощи стандартного шаблона модели, входящего в состав RDS. Использование шаблона упрощает написание модели блока, поскольку в этот шаблон уже включена связь с динамической переменной времени «DynTime», вычисление шага расчета и возможность чтения начальных условий с входов блока.

В §3.6.4.2 и §3.6.4.3 описаны модели блоков, которые численно рассчитывали процессы в системах, описываемых дифференциальными уравнениями (которые мы переводили в разностные), причем начальные условия этих уравнений подаются на входы блока. В каждой из этих моделей мы вычисляли шаг расчета как разность текущего времени и времени прошлого шага, а для определения правильного момента считывания начальных начальных условий мы использовали специальный логический флаг инициализации. Таким образом, в наших программах моделей динамических блоков содержатся похожие фрагменты, а в структуре статических переменных блока – переменные одинакового назначения. В составе стандартного модуля автокомпиляции есть шаблон динамического блока, в котором эти фрагменты и переменные уже введены, и разработчику остается только добавить свои переменные и дописать внутри модели две подпрограммы: чтения начальных условий с входов и выполнения вычислений для шага расчета. Во многих случаях это удобнее создания модели «с нуля».

Создание модели по шаблону отличается от создания пустой модели только на одном шаге: после нажатия кнопки «новый» на вкладке «компиляция» окна параметров блока, для которого создается модель в окне «новая модель» нужно вместо флажка «создать пустую модель» установить флажок «создать модель по шаблону» и выбрать название шаблона в списке под флажком (рис. 320).

В §3.6.4.2 рассматривалась модель блока, рассчитывающего траекторию тела, брошенного под углом к горизонту при наличии тяготения и отсутствии сопротивления воздуха. В окончательном варианте этой модели начальная скорость и угол броска считывались с входов блока «v0» и «Alpha» соответственно. Создадим точно такую же модель, но с использованием стандартного шаблона. Для этого необходимо выполнить следующие действия:

- нажать на свободном месте окна подсистемы правую кнопку мыши;
- выбрать в открывшемся контекстном меню пункт «создать | новый блок»;
- нажать на созданном блоке (он будет выглядеть как белый квадрат с черной рамкой) правую кнопку мыши;
- выбрать в открывшемся контекстном меню пункт «параметры»;
- на вкладке «общие» открывшегося окна включить флажок «по сигналу» на панели «запуск»;
- на вкладке «компиляция» установить флажок «функция блока компилируется автоматически», выбрать в выпадающем списке подключенный модуль автокомпиляции и нажать кнопку «новый»;
- в появившемся окне «новая модель» выбрать флажок «создать модель по шаблону», выбрать в списке шаблон «динамический блок» (см. рис. 320), а затем закрыть окно кнопкой «OK» (это единственное отличие от последовательности действий, которая выполняется при создании пустой модели);

Рис. 320 (повтор). Выбор создания модели по шаблону

- в диалоге сохранения модели ввести имя нового файла, в который будет записана создаваемая модель, и нажать кнопку «сохранить»;
- закрыть окно параметров блока кнопкой «OK».

После выполнения перечисленных выше действий откроется окно редактора, в котором вместо пустой вкладки «модель», которая открывалась при создании моделей «с нуля», будет открыта частично заполненная вкладка «описания в классе» (рис. 413).

Рис. 413. Окно редактора модели, созданной по стандартному шаблону

На этой вкладке размещается следующий текст:

      //---------------------------------------------------------
      // ВНИМАНИЕ! Переменные rdsbcppTime0, rdsbcppFirstStep и
      // DynTime нужны для работы блока, их не следует изменять
      // или удалять.
      //---------------------------------------------------------

      // Функции, заполняемые пользователем

      #pragma argsused
      void InitVars(double h)
      {
        // Здесь можно считать начальные условия с входов блока,
        // если это необходимо.
        // h - шаг расчета.
      }
      //---------------------------------------------------------

      #pragma argsused
      void DoStep(double h)
      {
        // Здесь можно вычислить новые значения выходов блока на
        // момент времени DynTime (интервал времени с прошлого
        // вычисления - h)
      }
      //---------------------------------------------------------

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

К модели блока уже подключена динамическая переменная времени «DynTime», структура статических переменных блока тоже уже частично заполнена. В ней находятся следующие переменные:

Имя | Тип | Вход/выход | Пуск | Начальное значение
--- | --- | --- | --- | ---
Start | Сигнал | Вход | ✓ | 0
Ready | Сигнал | Выход |  | 0
rdsbcppTime0 | double | Внутренняя |  | 0
rdsbcppFirstStep | Логический | Внутренняя |  | 1

Помимо стандартных сигналов «Start» и «Ready», которые есть у любого простого блока, в эту структуру добавлены две внутренние переменные: вещественная «rdsbcppTime0» и логическая «rdsbcppFirstStep». В переменной «rdsbcppTime0» хранится значение времени при прошлом срабатывании блока, необходимое для вычисления значения шага расчета – в моделях, которые мы создавали ранее, аналогичную переменную мы называли «t0». Логическая переменная «rdsbcppFirstStep» с единичным начальным значением используется как флаг инициализации блока – в предыдущих примерах моделей для этих целей мы использовали переменную «Init».

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

Рис. 414. События и описания модели,
созданной по шаблону

После этого в правой части окна редактора появится уже знакомая нам вкладка «модель» со следующим текстом:

      double h;
      if(DynTime==rdsbcppTime0) // Время не изменилось
        return;
      // Время изменилось
      h=DynTime-rdsbcppTime0; // Вычисление шага расчета

      if(rdsbcppFirstStep) // Инициализация
        { InitVars(h);
          rdsbcppFirstStep=0;
        }

      // Выполнение шага расчета
      DoStep(h);

      // Запоминание времени последнего расчета
      rdsbcppTime0=DynTime;

Этот текст уже знаком нам по предыдущим моделям, только раньше мы писали его вручную, а здесь он является частью стандартного шаблона. Как и раньше, сначала текущее время DynTime сравнивается с временем прошлого шага расчета rdsbcppTime0 и, если время не изменилось, модель немедленно завершается. Если время изменилось, вычисляется шаг расчета h и проверяется значение переменной rdsbcppFirstStep. Если оно равно единице, значит, выполняется самый первый шаг расчета – при этом вызывается функция InitVars из описаний в классе, внутрь которой мы должны вставить программу чтения начальных значений, после чего rdsbcppFirstStep сбрасывается. Затем вызывается функция DoStep, внутрь которой мы вставим вычисления значений переменных блока на новом шаге, и новое время запоминается в переменной rdsbcppTime0. При желании, в текст этой программы можно внести какие-либо изменения, но все, что необходимо для расчета, в ней уже есть, поэтому можно вернуться на вкладку «описания в классе» и наполнить содержимым две ее пустые функции. Но сначала добавим в блок необходимые для нашей модели переменные.

В рассмотренной в §3.6.4.2 модели свободного полета тела, получавшей начальные значения с входов блока, блок имел входы «v0» и «Alpha», на которые подавались начальная скорость в м/с и угол броска к горизонту в градусах соответственно, выходы «x» и «y», на которые выдавались текущие координаты летящего тела, и внутренние переменные «vx» и «vy», в которых вычислялась его текущая скорость, необходимая для расчета координат. Добавим эти переменные в структуру нашего блока, сохранив в ней те, которые являются частью шаблона. В результате этого наш блок будет иметь следующую структуру переменных:

Имя | Тип | Вход/выход | Пуск | Начальное значение
--- | --- | --- | --- | ---
Start | Сигнал | Вход | ✓ | 0
Ready | Сигнал | Выход |  | 0
rdsbcppTime0 | double | Внутренняя |  | 0
rdsbcppFirstStep | Логический | Внутренняя |  | 1
v0 | double | Вход |  | 0
Alpha | double | Вход |  | 0
x | double | Выход |  | 0
y | double | Выход |  | 0
vx | double | Внутренняя |  | 0
vy | double | Внутренняя |  | 0

Внутрь функции InitVars мы вставим фрагмент старой модели, отвечающий за чтение начальных условий:

      if(v0==rdsbcppHugeDouble || Alpha==rdsbcppHugeDouble)
        vx=vy=rdsbcppHugeDouble; // Ошибка на входе
      else
        { double alpha_rad=Alpha*M_PI/180; // В радианы
          // Вычисление начальных проекций скорости
          vx=v0*cos(alpha_rad);
          vy=v0*sin(alpha_rad);
        }

В нем мы вычисляем начальные значения проекций скорости vx и vy по данным на входах блока v0 и Alpha, если оба входа не содержат признака ошибки rdsbcppHugeDouble. Параметр функции h, в котором передается шаг расчета, для чтения начальных условий нам не нужен (директива препроцессора «#pragma argsused», расположенная перед описанием функции InitVars, подавит вывод предупреждения о неиспользуемом параметре).

Внутрь функции DoStep мы вставим фрагмент, вычислявший в старой модели новые значения x, y и vy (vx в данной задаче не изменяется со временем, поскольку в горизонтальном направлении на тело не действует никаких сил):

      double g=1.62; // Ускорение свободного падения на Луне
      x=x+h*vx;
      y=y+h*vy;
      vy=vy-h*g; // Новое vy вычисляем ПОСЛЕ нового y

Таким образом, если убрать поясняющие комментарии, находившиеся внутри функций InitVars и DoStep, содержимое вкладки «описания в классе» должно теперь выглядеть следующим образом:

      //---------------------------------------------------------
      // ВНИМАНИЕ! Переменные rdsbcppTime0, rdsbcppFirstStep и
      // DynTime нужны для работы блока, их не следует изменять
      // или удалять.
      //---------------------------------------------------------
      #pragma argsused
      void InitVars(double h)
      {
        if(v0==rdsbcppHugeDouble || Alpha==rdsbcppHugeDouble)
          vx=vy=rdsbcppHugeDouble; // Ошибка на входе
        else
          { double alpha_rad=Alpha*M_PI/180; // В радианы
            // Вычисление начальных проекций скорости
            vx=v0*cos(alpha_rad);
            vy=v0*sin(alpha_rad);
          }
      }
      //---------------------------------------------------------

      #pragma argsused
      void DoStep(double h)
      { double g=1.62; // Ускорение свободного падения на Луне
        x=x+h*vx;
        y=y+h*vy;
        vy=vy-h*g; // Новое vy вычисляем ПОСЛЕ нового y
      }
      //---------------------------------------------------------

Если теперь собрать с нашим новым блоком схему, аналогичную изображенной на рис. 406, и запустить расчет, на графике мы увидим ту же самую параболу: новая модель, созданная по шаблону, будет работать точно так же, как и старая, созданная вручную.

Важно понимать, что после создания по какому-либо шаблону, модель, с точки зрения RDS и модуля автокомпиляции, ничем не будет отличаться от модели, созданной вручную «с нуля». Никакой связи между моделью и шаблоном не сохраняется, шаблон – это просто некоторое начальное содержимое модели, которое может быть потом как угодно изменено пользователем. В рассмотренной модели, например, можно было бы стереть все описания в классе, а содержимое функций, которое мы туда вставили, разместить непосредственно на вкладке «модель» вместо вызовов этих функций. Можно было бы, при желании, переименовать переменные «rdsbcppTime0» и «rdsbcppFirstStep», являющиеся частью шаблона (например, дать им имена «t0» и «Init», как раньше), и в структуре статических переменных блока, и на вкладке «модель», где они используются. Ни то, ни другое на работоспособность блока не повлияло бы.

Пользователь может в любой момент сохранить любую свою модель в качестве шаблона, выбрав в меню редактора пункт «файл | сохранить как шаблон» (см. §3.5.1). Редактировать, добавлять и удалять шаблоны можно в настройках модуля автокомпиляции, эти действия рассматриваются в §3.8.2.