Поставим задачу не только вычислить веса и узлы,
но и проверить их на каком-нибудь простом интеграле. В качестве подынтегральной
функции выберем косинус:
Интегрирование будем осуществлять на
отрезке ![]()
, количество узлов положим равным
ста. Также отдельно зададим границы интервала ![]()
, чуть позже это будет полезным:
Получим узлы, находя корни полинома
Лежандра ![]()
сотой степени:
Посчитаем веса:
Найдем значение интеграла с помощью квадратурной
формулы Гаусса, учитывая, что интервал интегрирования не совпадает с интервалом
для этой формулы, а значит необходимо перераспределить узлы на новом отрезке,
масштабируя расстояния между ними:
Теперь вычислим интеграл стандартными средствами
Maple и сравним
результаты:
Последнее действие даёт единицу, что означает
адекватность узлов и весов.
5.3 Реализация на C++
Сейчас нужно экспортировать
полученные результаты в программу на С++. Для этого просто сохраним данные в
двух текстовых файлах - ![]()
и ![]()
и будем подгружать содержимое этих
файлов в самом начале работы программы с помощью функций:
Для того чтобы вычислить
коэффициенты ![]()
, которые с учётом соображений,
изложенных в пунктах ![]()
и ![]()
, определяются формулой:
также удобно воспользоваться
распараллеливанием. Как и в предыдущей главе, зададим функцию ![]()
в виде ![]()
, длину струны положим равной
единице, а количество коэффициентов ![]()
оставим равным двумстам пятидесяти
шести:
Расчёт будем осуществлять при помощи CUDA.
Создаём вспомогательную функцию, которая будет выполняться процессором и
отвечать за перенос данных из памяти CPU
в память GPU и обратно,
а также за вызов функции-ядра:
В качестве аргументов будем
передавать указатели на массивы, содержащие веса и узлы квадратурной формулы, а
также указатель на массив, в который будем сохранять получившиеся коэффициенты.
Внутри функции ![]()
прежде всего произведем копирование
данных из памяти процессора в память видеокарты:
а также выделим в памяти GPU
место под массив коэффициентов:
Необходимо посчитать всего двести пятьдесят
шесть коэффициентов, для чего будем использовать соответствующее число нитей.
Сгруппируем все эти нити в одномерные блоки по сто двадцать восемь нитей в
каждом и сформируем сетку из блоков:
Вызовем функцию-ядро, которая будет
называться ![]()
:
и после того, как она отработала, скопируем
результаты обратно в память процессора и освободим память видеокарты:
Функция ![]()
будет выполняться видеопроцессором
и осуществлять одни и те же действия для множества нитей. Вот её листинг:
Коэффициенты двумерного случая будем
представлять, как и в главе ![]()
, в виде произведения коэффициентов
каждого измерения. Удобно в такой ситуации сохранять получаемые результаты в
массиве типа ![]()
, где ![]()
-компонента будет содержать
коэффициенты, насчитанные для ![]()
-измерения, а ![]()
-компонента - для ![]()
Текст исполняющих функций при этом
меняется мало: массив ![]()
объявляется как ![]()
-массив, и это необходимо учитывать
каждый раз, когда осуществляется выделение места под коэффициенты, перенос
данных в память видеокарты или обратно. Листинг функции ![]()
:
В этом случае процесс вычисления
коэффициентов распараллеливается на двести пятьдесят шесть нитей, каждая из
которых выполняет расчёт одного ![]()
- и одного ![]()
-коэффициента. Можно поступить и
по-другому: не меняя типа массива ![]()
, сделать двумерной сетку и
двумерными же блоки нитей:
тогда процесс распараллеливается на ![]()
нитей, и каждая нить рассчитывает
только один коэффициент. Функция ![]()
будет выглядеть так:
В случае, если функция начального состояния мембраны по какой-то причине не представима в виде произведения двух независимых функций по двум измерениям, последний способ особенно удобен.
Итак, в этой главе был предложен метод получения входящих в решение коэффициентов с помощью квадратурной формулы Лежандра-Гаусса. В следующей главе рассмотрим оставшиеся до получения решения шаги.
6. ПАРАЛЛЕЛЬНЫЙ РАСЧЕТ АМПЛИТУД
6.1
Массивно-параллельный расчет амплитуд
Вернемся к решению задачи о колебаниях:
для которого уже найдены ![]()
. Так как бесконечное суммирование
осуществить программно, вообще говоря, невозможно, оборвем ряд на некотором ![]()
:
В четвертой и пятой главах при
расчёте коэффициентов ![]()
было задано равным двумстам
пятидесяти шести; при желании несложно сделать это число значительно больше, но
мы остановимся на этом значении.
Далее предлагается описывать
положение струны ![]()
точками и рассчитывать их
отклонения от положения равновесия в каждый момент времени, делая это с помощью
массивно-параллельных вычислений. Итак, разобьем всю длину струны ![]()
точками, тогда, если известен номер
![]()
точки, то её координату можно
выразить как ![]()
Создадим процедуру, которая
подготавливает запуск функции-ядра и осуществляет его:
В качестве аргументов она будет
принимать указатель на массив![]()
, куда будут записываться
рассчитанные амплитуды, указатель на массив коэффициентов, число коэффициентов,
число точек ![]()
и время, для которого необходимо
выполнить расчёт. Листинг функции: