1D Heat Equation Solver 1.0
Computational Methods Assignment 2025
Loading...
Searching...
No Matches
solver.cpp
Go to the documentation of this file.
1/**
2 * @file solver.cpp
3 * @brief Time-marching driver for the 1D heat equation.
4 */
5
6#include "solver.hpp"
7
8#include <algorithm> // std::fill
9
10HeatSolver::HeatSolver(const PhysParams& phys, const NumParams& num, SchemeKind scheme)
11 : phys_(phys)
12 , num_(num)
13 , grid_(phys.L_cm, num.dx)
14{
15 // Create the calculation method (factory based on scheme)
16 method_ = make_method(scheme);
17 uses_prev_step_ = method_->uses_previous_step();
18
19 // Vector allocation
20 T_.resize(grid_.Nx);
21 next_.resize(grid_.Nx);
23 {
24 Tprev_.resize(grid_.Nx);
25 }
26
27 // Field initialization
28 init_field();
29
30 // Bootstrap for 3-level methods (like DuFort-Frankel)
32}
33
34void HeatSolver::apply_dirichlet(std::vector<double>& T) const
35{
36 // Enforce surface temperature at both ends
37 T.front() = phys_.Tsur;
38 T.back() = phys_.Tsur;
39}
40
42{
43 std::fill(T_.begin(), T_.end(), phys_.Tin);
45}
46
48{
49 if (!uses_prev_step_)
50 {
51 return;
52 }
53
54 // We have T_ = T^0 here
55 // Tprev_ will store T^0
56 Tprev_ = T_;
57
58 // Construct T^1 with a small FTCS step (1D diffusion)
59 const double dx = grid_.dx;
60 const double r = phys_.D_cm2h * num_.dt / (dx * dx);
61 const std::size_t N = grid_.Nx;
62
63 next_ = T_;
64
65 // FTCS scheme (conditional stability: r <= 0.5)
66 // If r > 0.5, we just clone T0 to T1 to avoid oscillations at the first step
67 if (r <= 0.5)
68 {
69 for (std::size_t i = 1; i + 1 < N; ++i)
70 {
71 next_[i] = T_[i] + r * (T_[i + 1] - 2.0 * T_[i] + T_[i - 1]);
72 }
73 }
74 else
75 {
76 next_ = T_;
77 }
78
80 T_ = next_; // Now T_ holds T^1, Tprev_ holds T^0
81}
82
84 const std::function<void(double, const Grid&, const std::vector<double>&)>& onDump)
85{
86 double t = 0.0;
87 const double dt = num_.dt;
88 const double tEnd = num_.tEnd;
89 const double outEvery = num_.outEvery;
90 double nextDump = 0.0;
91
92 // -------------------------------------------------------------------------
93 // 1. Initial State Output
94 // -------------------------------------------------------------------------
95 onDump(t, grid_, T_);
96
97 // IMPORTANT FIX: Ensure 'next_' has BCs before calculating the first step.
98 // This prevents implicit schemes from seeing 0°C at boundaries instead of Tsur.
100
101 // -------------------------------------------------------------------------
102 // 2. Main Time Stepping Loop
103 // -------------------------------------------------------------------------
104 while (t < tEnd - 1e-12)
105 {
106 // --- A. Compute Next Time Step (T^{n+1}) ---
107 if (uses_prev_step_)
108 {
109 // Three-level method (e.g. DuFort-Frankel, Richardson)
110 method_->step(grid_, phys_.D_cm2h, dt, Tprev_, T_, next_);
111 }
112 else
113 {
114 // Two-level method (e.g. Laasonen, Crank-Nicolson)
115 // Note: For 2-level, the third argument (Tprev) is ignored by the method wrapper.
116 // We pass T_ (current) as Tprev just to satisfy the signature.
117 method_->step(grid_, phys_.D_cm2h, dt, T_, T_, next_);
118 }
119
120 // --- B. Enforce Boundary Conditions ---
121 // Overwrite boundaries just in case the method touched them (though it shouldn't)
123
124 // --- C. Shift Time States (Rotation) ---
125 if (uses_prev_step_)
126 {
127 Tprev_ = T_;
128 }
129 T_ = next_;
130
131 // --- D. Advance Time ---
132 t += dt;
133
134 // --- E. Periodic Output ---
135 if (t + 1e-12 >= nextDump)
136 {
137 onDump(t, grid_, T_);
138 nextDump += outEvery;
139 }
140 }
141}
std::vector< double > Tprev_
Previous solution (stores T^{n-1} when required).
Definition solver.hpp:40
PhysParams phys_
Physical parameters (copied for convenience).
Definition solver.hpp:33
void apply_dirichlet(std::vector< double > &T) const
Apply Dirichlet boundary conditions (Tsur) at both ends of the vector.
Definition solver.cpp:34
std::vector< double > next_
Buffer used to store the next time layer T^{n+1}.
Definition solver.hpp:41
NumParams num_
Numerical parameters (copied for convenience).
Definition solver.hpp:34
std::unique_ptr< Method > method_
Selected time integration method.
Definition solver.hpp:36
void bootstrap_if_needed()
Perform a warm-up step when the method needs two previous time levels.
Definition solver.cpp:47
void run(const std::function< void(double, const Grid &, const std::vector< double > &)> &onDump)
Run the simulation from t=0 to t=tEnd.
Definition solver.cpp:83
HeatSolver(const PhysParams &phys, const NumParams &num, SchemeKind scheme)
Construct the solver with physical and numerical parameters and a scheme kind.
Definition solver.cpp:10
Grid grid_
Spatial grid built from phys/num input.
Definition solver.hpp:35
std::vector< double > T_
Current solution (typically holds T^n).
Definition solver.hpp:39
void init_field()
Initialize the temperature field with Tin and enforce boundary conditions.
Definition solver.cpp:41
bool uses_prev_step_
True when the method requires T^{n-1}.
Definition solver.hpp:37
SchemeKind
Enumeration of the numerical schemes available in the solver.
Definition method.hpp:13
std::unique_ptr< Method > make_method(SchemeKind scheme)
Factory function to create a concrete method instance.
Definition method.cpp:27
High-level solver orchestrating BCs and time integration.
Uniform 1D grid on x ∈ [0, L], including both boundary nodes.
Definition grid.hpp:15
Numerical parameters controlling the discretization and output.
Definition types.hpp:20
Physical parameters of the wall heat conduction problem.
Definition types.hpp:10