Выполнил:
студент группы 851-М81
Меркулов А. Д.
ВВЕДЕНИЕ
ПОСТАНОВКА ЗАДАЧИ
ТЕОРЕТИЧЕСКИЕ ОСНОВЫ
3.1. Метод разделения переменных
3.2. Собственные функции и значения
АНАЛИТИЧЕСКОЕ РЕШЕНИЕ
4.1. Разложение начального условия
4.2. Окончательное решение
ФИЗИЧЕСКИЙ АНАЛИЗ
5.1. Распределение температуры
5.2. Тепловые потоки
5.3. Затухание гармоник
ВИЗУАЛИЗАЦИЯ РЕЗУЛЬТАТОВ
ВЫВОДЫ
ПРИЛОЖЕНИЕ А
Цель работы: Изучение методов решения смешанной задачи для однородного уравнения теплопроводности методом Фурье (разделения переменных)
Задачи исследования:
Актуальность исследования обусловлена фундаментальной важностью уравнения теплопроводности в математической физике и инженерных приложениях. Уравнение теплопроводности описывает процессы распространения тепла в твердых телах, диффузии веществ, и является основой для многих прикладных задач теплофизики.
Уравнение теплопроводности: U_t = 7U_xx
Начальное условие: U(x,0) = 6sin(2πx) + 7sin(3πx)
Граничные условия: U(0,t) = U(3,t) = 0
Область определения: x ∈ [0,3], t ≥ 0
Физическая интерпретация: Рассматривается задача о распределении температуры в однородном стержне длиной 3 единицы, с теплоизолированной боковой поверхностью, концы которого поддерживаются при нулевой температуре.
separation_of_variables: Метод разделения переменных: U(x,t) = X(x)T(t)
После подстановки в уравнение получаем две обыкновенные дифференциальные уравнения:
eigenfunctions: Собственные функции: X_n(x) = sin(nπx/L)
time_decay: Временная часть: T_n(t) = exp(-a(nπ/L)²t)
fourier_series: Решение: U(x,t) = Σ C_n sin(nπx/L) exp(-a(nπ/L)²t)
где:
Начальное условие: U(x,0) = 6sin(2πx) + 7sin(3πx)
Сравнивая с общим видом ряда Фурье:
U(x,0) = Σ Cₙ sin(nπx/L)
Находим коэффициенты:
Аналитическое решение: U(x,t) = 6·sin(2πx)·exp(-28π²t) + 7·sin(3πx)·exp(-63π²t)
Параметры гармоник:
Начальная энергия системы: 127.5000
Энергия через t=0.1: 0.0000
Потеря энергии: 127.5000
Скорость остывания: 1275.0000 ед.энергии/ед.времени
Согласно закону Фурье, тепловой поток пропорционален градиенту температуры:
q = -k·∂U/∂x
где k = 7 - коэффициент теплопроводности.
Время полураспада гармоник:
Наблюдение: Высокочастотные гармоники затухают быстрее низкочастотных.
Температура в характерных точках
| x | t | Температура U(x,t) |
|---|---|---|
| 0.5 | 0.000 | -7.000000 |
| 0.5 | 0.010 | -0.013955 |
| 0.5 | 0.020 | -0.000028 |
| 0.5 | 0.050 | -0.000000 |
| 0.5 | 0.100 | -0.000000 |
| 1.0 | 0.000 | 0.000000 |
| 1.0 | 0.010 | -0.000000 |
| 1.0 | 0.020 | -0.000000 |
| 1.0 | 0.050 | -0.000000 |
| 1.0 | 0.100 | -0.000000 |
Рисунок: Эволюция распределения температуры
Рисунок: Поверхность распределения температуры U(x,t)
Рисунок: Анализ тепловых потоков и энергии
Рисунок: Анимация распространения тепла в стержне
Примечание: Анимация демонстрирует процесс выравнивания температуры и затухания начального распределения.
Исходный код программы
# lab_heat_equation.py
# -*- coding: utf-8 -*-
"""
Лабораторная работа: Решение смешанной задачи для однородного уравнения теплопроводности
Решение уравнения теплопроводности методом Фурье
"""
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from typing import List, Tuple, Dict, Any
import sys
# Настройка стиля для академических графиков
plt.style.use('seaborn-v0_8-whitegrid')
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['font.size'] = 12
# Попытка импортировать python-docx для генерации .docx отчёта
try:
from docx import Document
from docx.shared import Inches, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
PYDOCX_AVAILABLE = True
except Exception:
PYDOCX_AVAILABLE = False
def read_own_source() -> Tuple[bool, str]:
"""
Пытается прочитать исходный код текущего файла и вернуть (успех, текст).
Возвращает (False, сообщение об ошибке) при неудаче.
"""
try:
current_file = os.path.abspath(__file__)
except Exception:
current_file = os.path.abspath(sys.argv[0]) if sys.argv and sys.argv[0] else None
if not current_file or not os.path.exists(current_file):
return False, "Не удалось определить путь к файлу скрипта"
try:
with open(current_file, "r", encoding="utf-8") as f:
src = f.read()
return True, src
except Exception as e:
return False, f"Ошибка при чтении исходного кода: {e}"
class HeatEquationAnalyzer:
"""Класс для решения уравнения теплопроводности методом Фурье"""
def __init__(self):
# Параметры задачи
self.equation = "U_t = 7U_xx"
self.initial_condition = "U(x,0) = 6sin(2πx) + 7sin(3πx)"
self.boundary_conditions = "U(0,t) = U(3,t) = 0"
self.domain = "x ∈ [0,3], t ≥ 0"
# Физические параметры
self.L = 3.0 # длина стержня
self.a = 7.0 # коэффициент теплопроводности
# Параметры вычислений
self.x_points = 200
self.t_points = 100
# Результаты вычислений
self.results = {}
def analytical_solution(self, x: np.ndarray, t: float) -> np.ndarray:
"""
Аналитическое решение методом Фурье
U(x,t) = 6·sin(2πx)·exp(-28π²t) + 7·sin(3πx)·exp(-63π²t)
"""
term1 = 6 * np.sin(2 * np.pi * x) * np.exp(-28 * np.pi**2 * t)
term2 = 7 * np.sin(3 * np.pi * x) * np.exp(-63 * np.pi**2 * t)
return term1 + term2
def initial_temperature(self, x: np.ndarray) -> np.ndarray:
"""Начальное распределение температуры"""
return 6 * np.sin(2 * np.pi * x) + 7 * np.sin(3 * np.pi * x)
def calculate_decay_rates(self) -> Dict[str, Any]:
"""Вычисление скоростей затухания гармоник"""
decay_rates = {
'mode1': {
'amplitude': 6,
'frequency': 2,
'decay_rate': 28 * np.pi**2,
'half_life': np.log(2) / (28 * np.pi**2)
},
'mode2': {
'amplitude': 7,
'frequency': 3,
'decay_rate': 63 * np.pi**2,
'half_life': np.log(2) / (63 * np.pi**2)
}
}
return decay_rates
def create_visualizations(self) -> Dict[str, str]:
"""Создание визуализаций для отчета"""
# Создаем папку для изображений
os.makedirs("results/images", exist_ok=True)
visualization_paths = {}
try:
# Создаем сетку для вычислений
x = np.linspace(0, self.L, self.x_points)
# 1. Начальное распределение температуры
plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1)
u_initial = self.initial_temperature(x)
plt.plot(x, u_initial, 'b-', linewidth=2)
plt.xlabel('x')
plt.ylabel('U(x,0)')
plt.title('Начальное распределение температуры')
plt.grid(True, alpha=0.3)
# 2. Распределение температуры в разные моменты времени
plt.subplot(2, 2, 2)
t_values = [0, 0.01, 0.02, 0.05, 0.1, 0.2]
colors = plt.cm.viridis(np.linspace(0, 1, len(t_values)))
for i, t in enumerate(t_values):
u = self.analytical_solution(x, t)
plt.plot(x, u, color=colors[i], linewidth=2, label=f't = {t}')
plt.xlabel('x')
plt.ylabel('U(x,t)')
plt.title('Эволюция температуры во времени')
plt.grid(True, alpha=0.3)
plt.legend()
# 3. Затухание амплитуд гармоник
plt.subplot(2, 2, 3)
t_decay = np.linspace(0, 0.1, 100)
amplitude1 = 6 * np.exp(-28 * np.pi**2 * t_decay)
amplitude2 = 7 * np.exp(-63 * np.pi**2 * t_decay)
plt.semilogy(t_decay, amplitude1, 'r-', linewidth=2, label='Первая гармоника (n=2)')
plt.semilogy(t_decay, amplitude2, 'b-', linewidth=2, label='Вторая гармоника (n=3)')
plt.xlabel('Время t')
plt.ylabel('Амплитуда')
plt.title('Затухание амплитуд гармоник')
plt.grid(True, alpha=0.3)
plt.legend()
# 4. Скорость изменения температуры в характерных точках
plt.subplot(2, 2, 4)
x_points = [0.25, 0.75, 1.25, 1.75, 2.25, 2.75]
t_temp = np.linspace(0, 0.1, 50)
for x_point in x_points:
temp_evolution = [self.analytical_solution(np.array([x_point]), t)[0] for t in t_temp]
plt.plot(t_temp, temp_evolution, linewidth=2, label=f'x = {x_point}')
plt.xlabel('Время t')
plt.ylabel('Температура U(x,t)')
plt.title('Эволюция температуры в точках')
plt.grid(True, alpha=0.3)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.suptitle('Решение уравнения теплопроводности', fontsize=16)
plt.tight_layout()
evolution_path = "results/images/temperature_evolution.png"
plt.savefig(evolution_path, dpi=300, bbox_inches='tight')
plt.close()
visualization_paths['temperature_evolution'] = evolution_path
# 5. Поверхность U(x,t) в пространстве
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')
X, T = np.meshgrid(x, np.linspace(0, 0.1, 50))
U = np.zeros_like(X)
for i in range(X.shape[0]):
for j in range(X.shape[1]):
U[i, j] = self.analytical_solution(X[i, j], T[i, j])
surf = ax.plot_surface(X, T, U, cmap='hot', alpha=0.8)
ax.set_xlabel('x')
ax.set_ylabel('t')
ax.set_zlabel('U(x,t)')
ax.set_title('Поверхность распределения температуры U(x,t)', fontsize=14)
fig.colorbar(surf, ax=ax, shrink=0.5, aspect=5)
surface_path = "results/images/temperature_surface.png"
plt.savefig(surface_path, dpi=300, bbox_inches='tight')
plt.close()
visualization_paths['temperature_surface'] = surface_path
# 6. Анимация распространения тепла
self.create_temperature_animation(x, visualization_paths)
# 7. Анализ тепловых потоков
self.create_heat_flow_analysis(x, visualization_paths)
except Exception as e:
print(f"Ошибка при создании визуализаций: {e}")
return visualization_paths
def create_temperature_animation(self, x: np.ndarray, visualization_paths: Dict[str, str]):
"""Создание анимации распространения тепла"""
try:
fig, ax = plt.subplots(figsize=(10, 6))
# Время для анимации
t_anim = np.linspace(0, 0.1, 100)
# Первый кадр
line, = ax.plot(x, self.analytical_solution(x, 0), 'b-', linewidth=2)
ax.set_xlim(0, self.L)
ax.set_ylim(-10, 10)
ax.set_xlabel('x')
ax.set_ylabel('U(x,t)')
ax.set_title('Распространение тепла в стержне')
ax.grid(True, alpha=0.3)
def animate(i):
t = t_anim[i]
u = self.analytical_solution(x, t)
line.set_ydata(u)
ax.set_title(f'Распространение тепла, t = {t:.3f}')
return line,
anim = animation.FuncAnimation(fig, animate, frames=len(t_anim),
interval=50, blit=True)
# Сохраняем анимацию
animation_path = "results/images/temperature_animation.gif"
anim.save(animation_path, writer='pillow', fps=20)
plt.close()
visualization_paths['temperature_animation'] = animation_path
print("Анимация распространения тепла создана!")
except Exception as e:
print(f"Ошибка при создании анимации: {e}")
def create_heat_flow_analysis(self, x: np.ndarray, visualization_paths: Dict[str, str]):
"""Анализ тепловых потоков"""
try:
plt.figure(figsize=(12, 8))
# 1. Градиент температуры в начальный момент
plt.subplot(2, 2, 1)
u_initial = self.initial_temperature(x)
# Численное дифференцирование для градиента
grad_u = np.gradient(u_initial, x)
plt.plot(x, grad_u, 'r-', linewidth=2)
plt.xlabel('x')
plt.ylabel('∂U/∂x')
plt.title('Градиент температуры в начальный момент')
plt.grid(True, alpha=0.3)
# 2. Тепловые потоки по закону Фурье (q = -k·∂U/∂x)
plt.subplot(2, 2, 2)
heat_flow = -self.a * grad_u # k = 7
plt.plot(x, heat_flow, 'g-', linewidth=2)
plt.xlabel('x')
plt.ylabel('Тепловой поток q')
plt.title('Распределение теплового потока (t=0)')
plt.grid(True, alpha=0.3)
# 3. Энергия системы во времени
plt.subplot(2, 2, 3)
t_energy = np.linspace(0, 0.1, 50)
energy = []
for t in t_energy:
u = self.analytical_solution(x, t)
# Энергия пропорциональна интегралу от U²
energy.append(np.trapezoid(u**2, x))
plt.plot(t_energy, energy, 'purple', linewidth=2)
plt.xlabel('Время t')
plt.ylabel('Энергия системы')
plt.title('Затухание энергии системы')
plt.grid(True, alpha=0.3)
# 4. Время релаксации для разных мод
plt.subplot(2, 2, 4)
decay_rates = self.calculate_decay_rates()
modes = ['Первая гармоника (n=2)', 'Вторая гармоника (n=3)']
half_lives = [decay_rates['mode1']['half_life'], decay_rates['mode2']['half_life']]
plt.bar(modes, half_lives, color=['red', 'blue'], alpha=0.7)
plt.ylabel('Время полураспада')
plt.title('Время затухания гармоник')
plt.grid(True, alpha=0.3)
plt.suptitle('Анализ тепловых процессов', fontsize=16)
plt.tight_layout()
heat_analysis_path = "results/images/heat_flow_analysis.png"
plt.savefig(heat_analysis_path, dpi=300, bbox_inches='tight')
plt.close()
visualization_paths['heat_flow_analysis'] = heat_analysis_path
except Exception as e:
print(f"Ошибка при анализе тепловых потоков: {e}")
def calculate_temperature_table(self) -> List[List[Any]]:
"""Вычисление таблицы температур в характерных точках"""
table_data = []
# Характерные точки и моменты времени
x_points = [0.5, 1.0, 1.5, 2.0, 2.5]
t_points = [0, 0.01, 0.02, 0.05, 0.1]
for x in x_points:
for t in t_points:
temperature = self.analytical_solution(np.array([x]), t)[0]
table_data.append([
f"{x:.1f}", f"{t:.3f}", f"{temperature:.6f}"
])
return table_data
def analyze(self):
"""Выполнение полного анализа уравнения теплопроводности"""
print("Решение уравнения теплопроводности методом Фурье...")
# Вычисление параметров решения
decay_rates = self.calculate_decay_rates()
print("Создание визуализаций...")
visualizations = self.create_visualizations()
print("Вычисление таблицы температур...")
temperature_table = self.calculate_temperature_table()
# Физический анализ
physical_analysis = self.physical_analysis()
# Сохранение результатов
self.results = {
'decay_rates': decay_rates,
'visualizations': visualizations,
'temperature_table': temperature_table,
'physical_analysis': physical_analysis,
'problem_definition': {
'equation': self.equation,
'initial_condition': self.initial_condition,
'boundary_conditions': self.boundary_conditions,
'domain': self.domain
},
'analytical_solution': {
'formula': "U(x,t) = 6·sin(2πx)·exp(-28π²t) + 7·sin(3πx)·exp(-63π²t)",
'modes': [
{'amplitude': 6, 'frequency': 2, 'decay': 28*np.pi**2},
{'amplitude': 7, 'frequency': 3, 'decay': 63*np.pi**2}
]
}
}
return self.results
def physical_analysis(self) -> Dict[str, Any]:
"""Физический анализ решения"""
# Начальная энергия
x = np.linspace(0, self.L, 1000)
u0 = self.initial_temperature(x)
initial_energy = np.trapezoid(u0**2, x)
# Энергия через время t=0.1
u_final = self.analytical_solution(x, 0.1)
final_energy = np.trapezoid(u_final**2, x)
# Скорость остывания
cooling_rate = (initial_energy - final_energy) / 0.1
return {
'initial_energy': initial_energy,
'final_energy': final_energy,
'energy_loss': initial_energy - final_energy,
'cooling_rate': cooling_rate,
'energy_conservation': 'Убывает со временем (диссипация)'
}
def get_analysis_description(self):
"""Возвращает описание анализа и методики"""
description = {
'title': "Решение смешанной задачи для однородного уравнения теплопроводности",
'discipline': "СПЕЦИАЛЬНЫЕ ГЛАВЫ МАТЕМАТИКИ",
'topic': "Решение уравнения теплопроводности методом Фурье",
'variant': "Вариант 6",
'purpose': "Изучение методов решения смешанной задачи для однородного уравнения теплопроводности методом Фурье (разделения переменных)",
'tasks': [
"Аналитическое решение методом разделения переменных",
"Определение собственных функций и значений",
"Вычисление коэффициентов разложения",
"Анализ физических процессов теплопередачи",
"Визуализация распределения температуры"
],
'methodology': {
'separation_of_variables': "Метод разделения переменных: U(x,t) = X(x)T(t)",
'eigenfunctions': "Собственные функции: X_n(x) = sin(nπx/L)",
'time_decay': "Временная часть: T_n(t) = exp(-a(nπ/L)²t)",
'fourier_series': "Решение: U(x,t) = Σ C_n sin(nπx/L) exp(-a(nπ/L)²t)"
},
'problem': {
'equation': self.equation,
'initial_condition': self.initial_condition,
'boundary_conditions': self.boundary_conditions,
'domain': self.domain
}
}
return description
class HeatEquationMarkdownReport:
"""Класс для генерации полного отчета в Markdown"""
def __init__(self, analyzer: HeatEquationAnalyzer, analysis_results: Dict[str, Any]):
self.analyzer = analyzer
self.results = analysis_results
def add_header(self, title: str, level: int = 1) -> str:
return f"{'#' * level} {title}\n\n"
def add_table(self, data: List[List[Any]], headers: List[str], title: str = "") -> str:
table_str = ""
if title:
table_str += f"**{title}**\n\n"
table_str += "| " + " | ".join(headers) + " |\n"
table_str += "|" + "|".join(["---"] * len(headers)) + "|\n"
for row in data:
table_str += "| " + " | ".join(map(str, row)) + " |\n"
table_str += "\n"
return table_str
def add_image(self, image_path: str, caption: str = "") -> str:
if os.path.exists(image_path):
return (f'<div align="center">\n\n'
f'<img src="{image_path}" alt="{caption}" style="max-width: 80%; height: auto; border: 1px solid #ddd; padding: 5px; background: #f8f9fa;">\n\n'
f'**Рисунок:** {caption}\n\n'
f'</div>\n\n')
else:
return f"*Изображение не найдено: {caption}*\n\n"
def add_code_section(self) -> str:
success, src = read_own_source()
section = self.add_header("ПРИЛОЖЕНИЕ А", 2)
section += "**Исходный код программы**\n\n"
if success:
section += "```python\n" + src
if not src.endswith("\n"):
section += "\n"
section += "```\n"
else:
section += f"*Не удалось прочитать исходный код: {src}*\n"
return section
def generate_report(self) -> str:
report = ""
# Титульная страница
report += '<div style="page-break-after: always;">\n\n'
report += "# Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования\n\n"
report += "### «Казанский национальный исследовательский технологический университет»\n\n"
report += "#### Институт: Институт управления, автоматизации и информационных технологий\n"
report += "#### Кафедра Информатики и прикладной математики\n\n"
report += "---\n\n"
report += "# ЛАБОРАТОРНАЯ РАБОТА\n\n"
description = self.analyzer.get_analysis_description()
report += f"### по дисциплине: «{description['discipline']}»\n\n"
report += f"### на тему: «{description['topic']}»\n\n"
report += f"### {description['variant']}\n\n"
report += "---\n\n"
report += "**Выполнил:** \n"
report += "студент группы 851-М81 \n"
report += "Меркулов А. Д. \n"
report += '</div>\n\n'
# Содержание
report += self.add_header("СОДЕРЖАНИЕ", 1)
toc = [
"1. ВВЕДЕНИЕ",
"2. ПОСТАНОВКА ЗАДАЧИ",
"3. ТЕОРЕТИЧЕСКИЕ ОСНОВЫ",
" 3.1. Метод разделения переменных",
" 3.2. Собственные функции и значения",
"4. АНАЛИТИЧЕСКОЕ РЕШЕНИЕ",
" 4.1. Разложение начального условия",
" 4.2. Окончательное решение",
"5. ФИЗИЧЕСКИЙ АНАЛИЗ",
" 5.1. Распределение температуры",
" 5.2. Тепловые потоки",
" 5.3. Затухание гармоник",
"6. ВИЗУАЛИЗАЦИЯ РЕЗУЛЬТАТОВ",
"7. ВЫВОДЫ",
"ПРИЛОЖЕНИЕ А"
]
for item in toc:
report += f"{item}\n\n"
report += "---\n\n"
# 1) Введение
report += self.add_header("1. ВВЕДЕНИЕ", 1)
report += f"**Цель работы:** {description['purpose']}\n\n"
report += "**Задачи исследования:**\n"
for i, task in enumerate(description['tasks'], 1):
report += f"{i}. {task}\n"
report += "\n"
report += "**Актуальность исследования** обусловлена фундаментальной важностью уравнения теплопроводности в математической физике и инженерных приложениях. Уравнение теплопроводности описывает процессы распространения тепла в твердых телах, диффузии веществ, и является основой для многих прикладных задач теплофизики.\n\n"
# 2) Постановка задачи
report += self.add_header("2. ПОСТАНОВКА ЗАДАЧИ", 1)
problem_def = description['problem']
report += f"**Уравнение теплопроводности:** {problem_def['equation']}\n\n"
report += f"**Начальное условие:** {problem_def['initial_condition']}\n\n"
report += f"**Граничные условия:** {problem_def['boundary_conditions']}\n\n"
report += f"**Область определения:** {problem_def['domain']}\n\n"
report += "**Физическая интерпретация:** Рассматривается задача о распределении температуры в однородном стержне длиной 3 единицы, с теплоизолированной боковой поверхностью, концы которого поддерживаются при нулевой температуре.\n\n"
# 3) Теоретические основы
report += self.add_header("3. ТЕОРЕТИЧЕСКИЕ ОСНОВЫ", 1)
report += self.add_header("3.1. Метод разделения переменных", 2)
methodology = description['methodology']
report += f"**{list(methodology.keys())[0]}:** {methodology['separation_of_variables']}\n\n"
report += "После подстановки в уравнение получаем две обыкновенные дифференциальные уравнения:\n"
report += "- X''(x) + λX(x) = 0\n"
report += "- T'(t) + aλT(t) = 0\n\n"
report += self.add_header("3.2. Собственные функции и значения", 2)
report += f"**{list(methodology.keys())[1]}:** {methodology['eigenfunctions']}\n\n"
report += f"**{list(methodology.keys())[2]}:** {methodology['time_decay']}\n\n"
report += f"**{list(methodology.keys())[3]}:** {methodology['fourier_series']}\n\n"
report += "где:\n"
report += "- Cₙ - коэффициенты, определяемые из начального условия\n"
report += "- L - длина стержня\n"
report += "- a - коэффициент температуропроводности\n"
report += "- λₙ = (nπ/L)² - собственные значения\n\n"
# 4) Аналитическое решение
report += self.add_header("4. АНАЛИТИЧЕСКОЕ РЕШЕНИЕ", 1)
analytical = self.results['analytical_solution']
report += self.add_header("4.1. Разложение начального условия", 2)
report += "Начальное условие: U(x,0) = 6sin(2πx) + 7sin(3πx)\n\n"
report += "Сравнивая с общим видом ряда Фурье:\n"
report += "U(x,0) = Σ Cₙ sin(nπx/L)\n\n"
report += "Находим коэффициенты:\n"
report += "- C₆ = 6 (соответствует sin(6πx/3) = sin(2πx))\n"
report += "- C₉ = 7 (соответствует sin(9πx/3) = sin(3πx))\n"
report += "- Остальные Cₙ = 0\n\n"
report += self.add_header("4.2. Окончательное решение", 2)
report += f"**Аналитическое решение:** {analytical['formula']}\n\n"
decay_rates = self.results['decay_rates']
report += "**Параметры гармоник:**\n"
report += f"- Первая гармоника: амплитуда = {decay_rates['mode1']['amplitude']}, частота = {decay_rates['mode1']['frequency']}π, скорость затухания = {decay_rates['mode1']['decay_rate']:.2f}\n"
report += f"- Вторая гармоника: амплитуда = {decay_rates['mode2']['amplitude']}, частота = {decay_rates['mode2']['frequency']}π, скорость затухания = {decay_rates['mode2']['decay_rate']:.2f}\n\n"
# 5) Физический анализ
report += self.add_header("5. ФИЗИЧЕСКИЙ АНАЛИЗ", 1)
physical = self.results['physical_analysis']
report += self.add_header("5.1. Распределение температуры", 2)
report += f"**Начальная энергия системы:** {physical['initial_energy']:.4f}\n"
report += f"**Энергия через t=0.1:** {physical['final_energy']:.4f}\n"
report += f"**Потеря энергии:** {physical['energy_loss']:.4f}\n"
report += f"**Скорость остывания:** {physical['cooling_rate']:.4f} ед.энергии/ед.времени\n\n"
report += self.add_header("5.2. Тепловые потоки", 2)
report += "Согласно закону Фурье, тепловой поток пропорционален градиенту температуры:\n"
report += "q = -k·∂U/∂x\n"
report += "где k = 7 - коэффициент теплопроводности.\n\n"
report += self.add_header("5.3. Затухание гармоник", 2)
report += "**Время полураспада гармоник:**\n"
report += f"- Первая гармоника: {decay_rates['mode1']['half_life']:.6f} с\n"
report += f"- Вторая гармоника: {decay_rates['mode2']['half_life']:.6f} с\n\n"
report += "**Наблюдение:** Высокочастотные гармоники затухают быстрее низкочастотных.\n\n"
# 6) Визуализация результатов
report += self.add_header("6. ВИЗУАЛИЗАЦИЯ РЕЗУЛЬТАТОВ", 1)
visualizations = self.results['visualizations']
# Таблица температур
table_data = self.results['temperature_table'][:10] # Первые 10 строк
report += self.add_table(table_data, ["x", "t", "Температура U(x,t)"],
"Температура в характерных точках")
if 'temperature_evolution' in visualizations:
report += self.add_image(visualizations['temperature_evolution'],
"Эволюция распределения температуры")
if 'temperature_surface' in visualizations:
report += self.add_image(visualizations['temperature_surface'],
"Поверхность распределения температуры U(x,t)")
if 'heat_flow_analysis' in visualizations:
report += self.add_image(visualizations['heat_flow_analysis'],
"Анализ тепловых потоков и энергии")
if 'temperature_animation' in visualizations:
report += self.add_image(visualizations['temperature_animation'],
"Анимация распространения тепла в стержне")
report += "**Примечание:** Анимация демонстрирует процесс выравнивания температуры и затухания начального распределения.\n\n"
# 7) Выводы
report += self.add_header("7. ВЫВОДЫ", 1)
conclusions = [
"Успешно решена смешанная задача для уравнения теплопроводности методом Фурье",
"Получено аналитическое решение в виде суперпозиции двух затухающих гармоник",
"Установлено, что высокочастотные гармоники затухают быстрее низкочастотных",
"Начальная энергия системы монотонно убывает со временем",
"Температура стремится к нулю во всех точках стержня при t → ∞",
"Граничные условия обеспечивают отвод тепла с концов стержня",
"Скорость остывания максимальна в начальный момент времени",
"Полученное решение удовлетворяет всем условиям задачи"
]
for i, conclusion in enumerate(conclusions, 1):
report += f"{i}. {conclusion}\n"
report += "\n"
# Приложение
report += self.add_code_section()
return report
class HeatEquationDocxReport:
"""Класс для генерации отчета в формате DOCX"""
def __init__(self, analyzer: HeatEquationAnalyzer, analysis_results: Dict[str, Any]):
self.analyzer = analyzer
self.results = analysis_results
def setup_document_styles(self, doc):
"""Настройка стилей документа"""
try:
doc.styles['Normal'].font.name = 'Times New Roman'
doc.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), 'Times New Roman')
doc.styles['Normal'].font.size = Pt(12)
except:
pass
def add_table_to_docx(self, doc, data: List[List[Any]], headers: List[str], title: str = ""):
"""Добавляет таблицу в DOCX документ"""
if title:
p = doc.add_paragraph()
p.add_run(title).bold = True
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.add_paragraph()
table = doc.add_table(rows=len(data)+1, cols=len(headers))
table.style = 'Table Grid'
# Заголовки
hdr_cells = table.rows[0].cells
for i, header in enumerate(headers):
hdr_cells[i].text = str(header)
hdr_cells[i].paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
for paragraph in hdr_cells[i].paragraphs:
for run in paragraph.runs:
run.bold = True
# Данные
for i, row in enumerate(data, 1):
row_cells = table.rows[i].cells
for j, cell in enumerate(row):
row_cells[j].text = str(cell)
if j > 0:
row_cells[j].paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.add_paragraph()
def add_image_to_docx(self, doc, image_path: str, caption: str = ""):
"""Добавляет изображение в DOCX документ"""
if os.path.exists(image_path):
try:
paragraph = doc.add_paragraph()
paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = paragraph.add_run()
run.add_picture(image_path, width=Inches(5.0))
if caption:
caption_paragraph = doc.add_paragraph()
caption_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
caption_run = caption_paragraph.add_run(f"Рисунок: {caption}")
caption_run.italic = True
doc.add_paragraph()
except Exception as e:
doc.add_paragraph(f"Ошибка при добавлении изображения: {e}")
else:
doc.add_paragraph(f"Изображение не найдено: {caption}")
def generate_docx_report(self, filename: str = "heat_equation_report.docx") -> str:
"""
Генерация отчёта в формате DOCX для уравнения теплопроводности
"""
out_path = os.path.join("results", filename)
if not PYDOCX_AVAILABLE:
print("python-docx не установлен — DOCX отчёт не будет создан")
return out_path
try:
doc = Document()
self.setup_document_styles(doc)
# Титульная страница
title_paragraph = doc.add_paragraph()
title_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
title_run = title_paragraph.add_run("Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования\n«Казанский национальный исследовательский технологический университет»")
title_run.bold = True
title_run.font.size = Pt(14)
doc.add_paragraph()
university_paragraph = doc.add_paragraph()
university_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
university_paragraph.add_run("Институт: Институт управления, автоматизации и информационных технологий\nКафедра Информатики и прикладной математики").bold = True
doc.add_paragraph("\n" * 3)
# Название работы
work_paragraph = doc.add_paragraph()
work_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
work_run = work_paragraph.add_run("ЛАБОРАТОРНАЯ РАБОТА")
work_run.bold = True
work_run.font.size = Pt(16)
description = self.analyzer.get_analysis_description()
doc.add_paragraph()
discipline_paragraph = doc.add_paragraph()
discipline_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
discipline_paragraph.add_run(f"по дисциплине: «{description['discipline']}»").bold = True
doc.add_paragraph()
topic_paragraph = doc.add_paragraph()
topic_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
topic_paragraph.add_run(f"на тему: «{description['topic']}»").bold = True
doc.add_paragraph()
variant_paragraph = doc.add_paragraph()
variant_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
variant_paragraph.add_run(f"{description['variant']}").bold = True
doc.add_paragraph("\n" * 4)
# Информация о студенте
info_table = doc.add_table(rows=2, cols=2)
info_table.style = 'Table Grid'
info_table.cell(0, 0).text = "Выполнил:"
info_table.cell(0, 1).text = "студент группы 851-М81\nМеркулов А. Д."
for row in info_table.rows:
for cell in row.cells:
cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.LEFT
doc.add_page_break()
# Содержание
title = doc.add_paragraph()
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
title.add_run("СОДЕРЖАНИЕ").bold = True
title.runs[0].font.size = Pt(14)
doc.add_paragraph()
toc_items = [
"1. ВВЕДЕНИЕ",
"2. ПОСТАНОВКА ЗАДАЧИ",
"3. ТЕОРЕТИЧЕСКИЕ ОСНОВЫ",
"4. АНАЛИТИЧЕСКОЕ РЕШЕНИЕ",
"5. ФИЗИЧЕСКИЙ АНАЛИЗ",
"6. ВИЗУАЛИЗАЦИЯ РЕЗУЛЬТАТОВ",
"7. ВЫВОДЫ",
"ПРИЛОЖЕНИЕ А"
]
for item in toc_items:
p = doc.add_paragraph()
p.add_run(item)
doc.add_page_break()
# Основное содержание
visualizations = self.results['visualizations']
# 1) Введение
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("1. ВВЕДЕНИЕ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
doc.add_paragraph(f"Цель работы: {description['purpose']}")
doc.add_paragraph("Задачи исследования:")
for task in description['tasks']:
p = doc.add_paragraph(task, style='List Bullet')
# 2) Постановка задачи
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("2. ПОСТАНОВКА ЗАДАЧИ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
problem_def = description['problem']
doc.add_paragraph(f"Уравнение теплопроводности: {problem_def['equation']}")
doc.add_paragraph(f"Начальное условие: {problem_def['initial_condition']}")
doc.add_paragraph(f"Граничные условия: {problem_def['boundary_conditions']}")
doc.add_paragraph(f"Область определения: {problem_def['domain']}")
# 3) Теоретические основы
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("3. ТЕОРЕТИЧЕСКИЕ ОСНОВЫ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
methodology = description['methodology']
doc.add_paragraph(f"Метод разделения переменных: {methodology['separation_of_variables']}")
doc.add_paragraph(f"Собственные функции: {methodology['eigenfunctions']}")
# 4) Аналитическое решение
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("4. АНАЛИТИЧЕСКОЕ РЕШЕНИЕ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
analytical = self.results['analytical_solution']
doc.add_paragraph(f"Аналитическое решение: {analytical['formula']}")
# 5) Физический анализ
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("5. ФИЗИЧЕСКИЙ АНАЛИЗ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
physical = self.results['physical_analysis']
doc.add_paragraph(f"Начальная энергия: {physical['initial_energy']:.4f}")
doc.add_paragraph(f"Энергия через t=0.1: {physical['final_energy']:.4f}")
# 6) Визуализация
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("6. ВИЗУАЛИЗАЦИЯ РЕЗУЛЬТАТОВ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
if 'temperature_evolution' in visualizations:
self.add_image_to_docx(doc, visualizations['temperature_evolution'],
"Эволюция распределения температуры")
if 'temperature_surface' in visualizations:
self.add_image_to_docx(doc, visualizations['temperature_surface'],
"Поверхность распределения температуры")
if 'heat_flow_analysis' in visualizations:
self.add_image_to_docx(doc, visualizations['heat_flow_analysis'],
"Анализ тепловых потоков")
# 7) Выводы
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("7. ВЫВОДЫ").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
conclusions = [
"Получено аналитическое решение уравнения теплопроводности",
"Исследовано распределение температуры во времени",
"Проанализированы тепловые потоки и энергия системы",
"Визуализированы результаты решения"
]
for i, conclusion in enumerate(conclusions, 1):
doc.add_paragraph(f"{i}. {conclusion}", style='List Number')
# Приложение
doc.add_page_break()
heading = doc.add_paragraph()
heading.alignment = WD_ALIGN_PARAGRAPH.CENTER
heading.add_run("ПРИЛОЖЕНИЕ А").bold = True
heading.runs[0].font.size = Pt(14)
doc.add_paragraph()
subheading = doc.add_paragraph()
subheading.add_run("Исходный код программы").bold = True
success_src, src_text = read_own_source()
if success_src:
for line in src_text.splitlines():
p = doc.add_paragraph()
run = p.add_run(line)
try:
run.font.name = 'Courier New'
run.font.size = Pt(9)
except Exception:
pass
else:
doc.add_paragraph("Не удалось прочитать исходный код скрипта: " + src_text)
# Сохранение файла
doc.save(out_path)
print(f"DOCX-отчёт сохранён: {out_path}")
except Exception as e:
print(f"Ошибка при генерации DOCX-отчёта: {e}")
raise e
return out_path
def main():
"""Основная функция для выполнения лабораторной работы по уравнению теплопроводности"""
# Создаем анализатор
analyzer = HeatEquationAnalyzer()
# Выполняем анализ
print("Решение уравнения теплопроводности методом Фурье...")
results = analyzer.analyze()
# Создаем папку для результатов
os.makedirs("results", exist_ok=True)
os.makedirs("results/images", exist_ok=True)
# Генерируем Markdown отчет
md_reporter = HeatEquationMarkdownReport(analyzer, results)
md_report = md_reporter.generate_report()
# Сохраняем Markdown отчет
with open("README.md", "w", encoding="utf-8") as f:
f.write(md_report)
print("Markdown отчет сохранен: README.md")
# Генерируем DOCX отчет
docx_reporter = HeatEquationDocxReport(analyzer, results)
docx_path = docx_reporter.generate_docx_report()
# Выводим основные результаты в консоль
print("\n" + "="*80)
print("РЕЗУЛЬТАТЫ РЕШЕНИЯ УРАВНЕНИЯ ТЕПЛОПРОВОДНОСТИ")
print("="*80)
analytical = results['analytical_solution']
decay_rates = results['decay_rates']
physical = results['physical_analysis']
print(f"\nАНАЛИТИЧЕСКОЕ РЕШЕНИЕ:")
print("-" * 50)
print(analytical['formula'])
print(f"\nПАРАМЕТРЫ ГАРМОНИК:")
print("-" * 50)
print(f"Первая гармоника: амплитуда={decay_rates['mode1']['amplitude']}, "
f"частота={decay_rates['mode1']['frequency']}π, "
f"затухание={decay_rates['mode1']['decay_rate']:.2f}")
print(f"Вторая гармоника: амплитуда={decay_rates['mode2']['amplitude']}, "
f"частота={decay_rates['mode2']['frequency']}π, "
f"затухание={decay_rates['mode2']['decay_rate']:.2f}")
print(f"\nФИЗИЧЕСКИЙ АНАЛИЗ:")
print("-" * 50)
print(f"Начальная энергия: {physical['initial_energy']:.4f}")
print(f"Энергия через t=0.1: {physical['final_energy']:.4f}")
print(f"Потеря энергии: {physical['energy_loss']:.4f}")
print(f"\nВИЗУАЛИЗАЦИИ СОЗДАНЫ:")
print("-" * 50)
for name, path in results['visualizations'].items():
if os.path.exists(path):
print(f"✓ {name}: {path}")
print(f"\nОТЧЕТЫ СОХРАНЕНЫ:")
print("-" * 50)
print(f"✓ Markdown отчет: README.md")
if PYDOCX_AVAILABLE:
print(f"✓ DOCX отчет: {docx_path}")
return results
if __name__ == "__main__":
main()