Загрузка данных
/**
* ============================================================
* МЕТОД СЕТОК (Конечно-разностный метод)
* Решение уравнения Пуассона на прямоугольной области
*
* Уравнение: d²u/dx² + d²u/dy² = f(x, y)
* Область: (x, y) ∈ [0, a] × [0, b]
* Граничные условия Дирихле: u|_Γ = g(x, y)
*
* Метод решения: Зейделя с верхней релаксацией (SOR)
* ============================================================
*/
#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>
#include <fstream>
#include <string>
#include <chrono>
using namespace std;
// ============================================================
// ОПРЕДЕЛЕНИЕ π (кроссплатформенное решение)
// ============================================================
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// ============================================================
// НАСТРОЙКИ ЗАДАЧИ
// ============================================================
// Параметры области
const double A = 1.0; // Размер по x: [0, A]
const double B = 1.0; // Размер по y: [0, B]
// Параметры сетки
const int NX = 30; // Количество узлов по x (включая границы)
const int NY = 30; // Количество узлов по y (включая границы)
// Параметры итерационного процесса
const double EPS = 1e-6; // Точность сходимости
const int MAX_ITER = 100000; // Максимальное число итераций
const double OMEGA = 1.5; // Параметр релаксации SOR (1 < w < 2)
// ============================================================
// ФУНКЦИИ ЗАДАЧИ
// ============================================================
/**
* Правая часть уравнения Пуассона: f(x, y)
*
* Тестовый пример:
* Точное решение: u(x,y) = sin(pi*x) * sin(pi*y)
* Тогда: f(x,y) = -2*pi^2 * sin(pi*x) * sin(pi*y)
*/
double f(double x, double y) {
return -2.0 * M_PI * M_PI * sin(M_PI * x) * sin(M_PI * y);
}
/**
* Граничное условие: g(x, y) на границе Γ
* Для тестового примера: u = 0 на всей границе
*/
double g(double x, double y) {
return 0.0;
}
/**
* Точное решение (для проверки точности)
*/
double exact_solution(double x, double y) {
return sin(M_PI * x) * sin(M_PI * y);
}
// ============================================================
// КЛАСС: Сеточный решатель
// ============================================================
class GridSolver {
private:
int nx, ny; // Число узлов сетки
double a, b; // Размеры области
double hx, hy; // Шаги сетки
double omega; // Параметр релаксации
double eps; // Точность
int max_iter; // Макс. итераций
vector<vector<double>> u; // Решение
vector<vector<double>> f_val; // Значения правой части
public:
// ----------------------------------------------------------
// Конструктор
// ----------------------------------------------------------
GridSolver(int nx_, int ny_, double a_, double b_,
double omega_, double eps_, int max_iter_)
: nx(nx_), ny(ny_), a(a_), b(b_),
omega(omega_), eps(eps_), max_iter(max_iter_)
{
hx = a / (nx - 1);
hy = b / (ny - 1);
// Инициализация массивов
u.assign(nx, vector<double>(ny, 0.0));
f_val.assign(nx, vector<double>(ny, 0.0));
// Вычисление значений правой части во внутренних узлах
for (int i = 1; i < nx - 1; i++) {
for (int j = 1; j < ny - 1; j++) {
double x = i * hx;
double y = j * hy;
f_val[i][j] = f(x, y);
}
}
// Установка граничных условий
set_boundary_conditions();
}
// ----------------------------------------------------------
// Установка граничных условий Дирихле
// ----------------------------------------------------------
void set_boundary_conditions() {
for (int i = 0; i < nx; i++) {
for (int j = 0; j < ny; j++) {
if (i == 0 || i == nx - 1 || j == 0 || j == ny - 1) {
double x = i * hx;
double y = j * hy;
u[i][j] = g(x, y);
}
}
}
}
// ----------------------------------------------------------
// Метод Зейделя с верхней релаксацией (SOR)
//
// Конечно-разностная схема (5-точечный шаблон):
//
// (u[i+1][j] - 2u[i][j] + u[i-1][j]) / hx^2 +
// (u[i][j+1] - 2u[i][j] + u[i][j-1]) / hy^2 = f[i][j]
// ----------------------------------------------------------
int solve() {
double cx = 1.0 / (hx * hx);
double cy = 1.0 / (hy * hy);
double denominator = 2.0 * cx + 2.0 * cy;
int iter = 0;
double max_diff;
cout << "Запуск итерационного процесса (SOR, w = "
<< omega << ")..." << endl;
for (iter = 1; iter <= max_iter; iter++) {
max_diff = 0.0;
for (int i = 1; i < nx - 1; i++) {
for (int j = 1; j < ny - 1; j++) {
// Новое значение по формуле Зейделя
double u_new = (
(u[i + 1][j] + u[i - 1][j]) * cx +
(u[i][j + 1] + u[i][j - 1]) * cy -
f_val[i][j]
) / denominator;
// SOR: релаксация
u_new = omega * u_new + (1.0 - omega) * u[i][j];
// Отслеживание максимальной невязки
double diff = fabs(u_new - u[i][j]);
if (diff > max_diff) {
max_diff = diff;
}
u[i][j] = u_new;
}
}
// Вывод прогресса каждые 500 итераций
if (iter % 500 == 0) {
cout << " Итерация " << setw(6) << iter
<< " | max|du| = " << scientific
<< setprecision(4) << max_diff << endl;
}
// Проверка сходимости
if (max_diff < eps) {
break;
}
}
return iter;
}
// ----------------------------------------------------------
// Вычисление погрешности
// ----------------------------------------------------------
double compute_error() {
double max_error = 0.0;
for (int i = 0; i < nx; i++) {
for (int j = 0; j < ny; j++) {
double x = i * hx;
double y = j * hy;
double error = fabs(u[i][j] - exact_solution(x, y));
if (error > max_error) {
max_error = error;
}
}
}
return max_error;
}
// ----------------------------------------------------------
// Вывод решения в консоль (тепловая карта)
// ----------------------------------------------------------
void print_heatmap() {
cout << "\n=== ТЕПЛОВАЯ КАРТА РЕШЕНИЯ ===" << endl;
cout << "(символы: ' ' < '.' < ':' < '-' < '=' < '+' < '*' < '#')"
<< endl << endl;
const string chars = " .:-=+*#%@";
int levels = (int)chars.size();
// Находим min/max для масштабирования
double u_min = 1e18, u_max = -1e18;
for (int i = 0; i < nx; i++) {
for (int j = 0; j < ny; j++) {
if (u[i][j] < u_min) u_min = u[i][j];
if (u[i][j] > u_max) u_max = u[i][j];
}
}
double range = u_max - u_min;
if (range < 1e-15) range = 1.0;
// Вывод (y сверху вниз для корректного отображения)
for (int j = ny - 1; j >= 0; j--) {
for (int i = 0; i < nx; i++) {
int level = (int)((u[i][j] - u_min) / range * (levels - 1));
if (level < 0) level = 0;
if (level >= levels) level = levels - 1;
cout << chars[level];
}
if (j == ny - 1) cout << " y = " << fixed << setprecision(2) << b;
if (j == 0) cout << " y = 0";
cout << endl;
}
cout << "x = 0"
<< string(nx > 10 ? nx - 10 : 0, ' ')
<< "x = " << fixed << setprecision(2) << a << endl;
cout << "\nМинимум: " << scientific << u_min
<< " Максимум: " << u_max << endl;
}
// ----------------------------------------------------------
// Вывод числовой таблицы
// ----------------------------------------------------------
void print_table(int step = 1) {
cout << "\n=== ЧИСЛОВАЯ ТАБЛИЦА РЕШЕНИЯ ===" << endl;
cout << setw(8) << "x\\y";
for (int j = 0; j < ny; j += step) {
cout << setw(10) << fixed << setprecision(3) << j * hy;
}
cout << endl;
int cols = (ny - 1) / step + 1;
cout << string(8 + 10 * cols, '-') << endl;
for (int i = 0; i < nx; i += step) {
cout << setw(8) << fixed << setprecision(3) << i * hx;
for (int j = 0; j < ny; j += step) {
cout << setw(10) << fixed << setprecision(5) << u[i][j];
}
cout << endl;
}
}
// ----------------------------------------------------------
// Экспорт в файл
// ----------------------------------------------------------
void export_to_file(const string& filename) {
ofstream file(filename);
if (!file.is_open()) {
cerr << "Ошибка: не удалось открыть файл " << filename << endl;
return;
}
file << "# x y u(x,y) u_exact error" << endl;
for (int i = 0; i < nx; i++) {
for (int j = 0; j < ny; j++) {
double x = i * hx;
double y = j * hy;
file << fixed << setprecision(8)
<< x << " " << y << " "
<< u[i][j] << " "
<< exact_solution(x, y) << " "
<< fabs(u[i][j] - exact_solution(x, y))
<< endl;
}
file << endl;
}
file.close();
cout << "Результаты экспортированы в файл: " << filename << endl;
}
// ----------------------------------------------------------
// Информация о сетке
// ----------------------------------------------------------
void print_info() {
cout << "\n+--------------------------------------------------+" << endl;
cout << "| МЕТОД СЕТОК - УРАВНЕНИЕ ПУАССОНА |" << endl;
cout << "+--------------------------------------------------+" << endl;
cout << "| Область: [0, " << A << "] x [0, " << B << "]"
<< string(20, ' ') << "|" << endl;
cout << "| Сетка: " << nx << " x " << ny << " узлов"
<< string(24, ' ') << "|" << endl;
cout << "| Шаг hx: " << fixed << setprecision(6) << hx
<< string(18, ' ') << "|" << endl;
cout << "| Шаг hy: " << fixed << setprecision(6) << hy
<< string(18, ' ') << "|" << endl;
cout << "| Точность: " << scientific << eps
<< string(18, ' ') << "|" << endl;
cout << "| Параметр w: " << fixed << setprecision(2) << omega
<< string(23, ' ') << "|" << endl;
cout << "+--------------------------------------------------+" << endl;
}
};
// ============================================================
// ИССЛЕДОВАНИЕ СХОДИМОСТИ ПО РАЗМЕРУ СЕТКИ
// ============================================================
void convergence_study() {
cout << "\n+--------------------------------------------------+" << endl;
cout << "| ИССЛЕДОВАНИЕ СХОДИМОСТИ ПО СЕТКЕ |" << endl;
cout << "+--------------------------------------------------+" << endl;
cout << "| N | hx | Итераций | Max погрешность |" << endl;
cout << "+-------+-----------+-----------+------------------+" << endl;
vector<int> grid_sizes = {10, 15, 20, 30, 40, 50};
for (int n : grid_sizes) {
GridSolver solver(n, n, A, B, OMEGA, EPS, MAX_ITER);
int iters = solver.solve();
double error = solver.compute_error();
cout << "| " << setw(3) << n << " | "
<< fixed << setprecision(5) << A / (n - 1) << " | "
<< setw(7) << iters << " | "
<< scientific << setprecision(4) << error
<< " |" << endl;
}
cout << "+--------------------------------------------------+" << endl;
}
// ============================================================
// ГЛАВНАЯ ФУНКЦИЯ
// ============================================================
int main() {
setlocale(LC_ALL, "Russian");
cout << "=====================================================" << endl;
cout << " ЧИСЛЕННЫЕ МЕТОДЫ: МЕТОД СЕТОК (ВАРИАНТ 2)" << endl;
cout << " Решение уравнения Пуассона методом SOR" << endl;
cout << "=====================================================" << endl;
// ---- Основной расчёт ----
GridSolver solver(NX, NY, A, B, OMEGA, EPS, MAX_ITER);
solver.print_info();
// Замер времени
auto start = chrono::high_resolution_clock::now();
int iterations = solver.solve();
auto end = chrono::high_resolution_clock::now();
double elapsed = chrono::duration<double, milli>(end - start).count();
// ---- Результаты ----
double max_error = solver.compute_error();
cout << "\n+--------------------------------------------------+" << endl;
cout << "| РЕЗУЛЬТАТЫ |" << endl;
cout << "+--------------------------------------------------+" << endl;
cout << "| Итераций выполнено: " << setw(8) << iterations
<< string(18, ' ') << "|" << endl;
cout << "| Время вычислений: " << fixed << setprecision(2)
<< elapsed << " мс" << string(16, ' ') << "|" << endl;
cout << "| Макс. погрешность: " << scientific << setprecision(6)
<< max_error << string(8, ' ') << "|" << endl;
cout << "| Порядок точности: O(h^2) = O("
<< fixed << setprecision(6) << (A / (NX - 1)) * (A / (NX - 1))
<< ")" << string(6, ' ') << "|" << endl;
cout << "+--------------------------------------------------+" << endl;
// ---- Визуализация ----
solver.print_heatmap();
// Числовая таблица (с шагом 5 для читаемости)
solver.print_table(5);
// ---- Экспорт ----
solver.export_to_file("grid_solution.dat");
// ---- Исследование сходимости ----
convergence_study();
cout << "\nПрограмма завершена." << endl;
return 0;
}