Фрактал «магнитный маятник» - Основные уравнения

ОГЛАВЛЕНИЕ

Основные уравнения

Движение маятника вычисляется путем двойного интегрирования по ускорениям, действующим на маятник. Обычно бы речь шла не об ускорениях, а о силах. Согласно первому закону Ньютона, сила, необходимая для перемещения тела, равна произведению массы на ускорение. Это уравнение было решено для ускорения.

 

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

Код для прослеживания траектории маятника

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

  пока прослеживается траектория маятника
    положение += скорость
    ускорение = 0

    для всех источников силы
    ускорение += ускорение, вызванное источником

    если (маятник рядом с источником, и скорость маленькая) то
      магнит останова = индекс источника
      прервать
    конец ветвления if
    конец цикла for

    ускорение -= ускорение, вызванное трением
    скорость     += ускорение
    длина траектории += длина(скорость)

    сохранить магнит останова
    сохранить длину траектории
  конец цикла while

Реализация кода для прослеживания траектории, выбранной маятником, на C++ выглядит так:

for (int ct=0; ct<m_nMaxSteps && bRunning; ++ct)
{
  // вычислить новое положение
  pos[0] += vel[0]*dt + sqr(dt) * (2.0/3.0 *
           (*acc)[0] - 1.0 / 6.0 * (*acc_p)[0]);
  pos[1] += vel[1]*dt + sqr(dt) * (2.0/3.0 *
           (*acc)[1] - 1.0 / 6.0 * (*acc_p)[1]);

  (*acc_n) = 0.0;  // сбросить ускорение

  // Вычислить силу, речь идет о силах, пропорциональных
  // расстоянию или обратному квадрату расстояния
  for (std::size_t i=0; i<src_num; ++i)
  {
    const Source &src( m_vSources[i] );
    r = pos - src.pos;
    if (src.type==Source::EType::tpLIN)
    {
      //---------------------------------------
      // Закон Гука:          _
      //         _             r         _  
      //  m * a = - k * |r| * --- = -k * r
      //                      |r|
      //
      (*acc_n)[0] -= src.mult * r[0];
      (*acc_n)[1] -= src.mult * r[1];
    }
    else
    {    
      //---------------------------------------
      // Магнитные силы: _
      //      _         r
      //  m * a = k * -----
      //               |r?|
      //
      double dist( sqrt( sqr(src.pos[0] - pos[0]) + 
                         sqr(src.pos[1] - pos[1]) +
                         sqr(m_fHeight) ) );
      (*acc_n)[0] -= (src.mult / (dist*dist*dist)) * r[0];
      (*acc_n)[1] -= (src.mult / (dist*dist*dist)) * r[1];
    }

    // Проверить условие прерывания
    if (ct>m_nMinSteps && abs(r)<src.size && abs(vel)<m_fAbortVel)
    {
      bRunning = false;
      stop_mag = (int)i;
      break;
    }
  } // для всех источников

  //--------------------------------------------------------------
  // 3.) Трение пропорционально скорости
  (*acc_n)[0] -= vel[0] * m_fFriction;
  (*acc_n)[1] -= vel[1] * m_fFriction;   

  //--------------------------------------------------------------
  // 4.) Интегрирование Бимена для скоростей
  vel[0] += dt * ( 1.0/3.0 * (*acc_n)[0] + 5.0/6.0 *
                 (*acc)[0] - 1.0/6.0 * (*acc_p)[0] );
  vel[1] += dt * ( 1.0/3.0 * (*acc_n)[1] + 5.0/6.0 *
                 (*acc)[1] - 1.0/6.0 * (*acc_p)[1] );

  //--------------------------------------------------------------
  // 5.) перевернуть буфер ускорений
  tmp = acc_p;
  acc_p = acc;
  acc   = acc_n;
  acc_n = tmp;
};