Problem: C++ 1D Advection Equation Solver Utilities

The problem requires implementing a comprehensive suite of C++ utilities for 1D advection equation numerical simulation, including core finite difference solvers, boundary condition processing, adaptive time stepping logic, stability checks, error analysis, and benchmark verification tools, all packaged as function pointers in returnable structs for numerical research and simulation applications.


Input/Output: The problem receives the following input data: double-type advection velocity c, double-type spatial grid step dx, double-type total simulation time T, string-type initial condition identifier (valid values: square, gaussian, step), string-type boundary condition identifier (valid values: zero, periodic, reflective), string-type numerical scheme identifier (valid values: leap_frog, lax_wendroff), string-type source term identifier (valid values: point, spacetime), optional size_t point source position, double-type maximum allowed CFL number, optional double-type initial time step dt, optional std::vector<double> reference analytical solution for error assessment.
The problem outputs a struct containing type-safe function pointers for all solver, utility, error analysis and stability verification functions, std::vector<std::vector<double>> time-series snapshots of the advected field, double-type calculated CFL value, double-type adaptively adjusted time step, double-type L2 and maximum error values, boolean stability status, double-type numerical order of accuracy, and boolean benchmark verification result. Each input parameter set maps to a unique set of output results corresponding to the requested simulation configuration.

Required dependencies:

Sub-steps:
step_number: 1
step_description_prompt: Please use C++ to generate a function named subproblem_9_1_functions. Implement 1D advection solvers including leapfrog and Lax-Wendroff steps, boundary condition handling, initial condition generation, CFL calculation, adaptive time stepping, and a complete solving wrapper, return all corresponding function pointers via a struct.
function_header: auto subproblem_9_1_functions() {
    struct Result
test_cases: int main() {
    unsigned int seed = 12345;
    srand(seed);
    
    auto funcs = subproblem_9_1_functions();
    auto leap_frog_step = funcs.leap_frog_step;
    auto calculate_cfl = funcs.calculate_cfl;
    
    bool test1_passed = false, test2_passed = false, test3_passed = false;
    
    {
        double c = 0.99, dt = 0.99, dx = 1.0;
        double cfl = calculate_cfl(c, dt, dx);
        
        size_t grid_size = 200;
        std::vector<double> phi_prev(grid_size, 0.0);
        std::vector<double> phi_curr(grid_size, 0.0);
        std::vector<double> source(grid_size, 0.0);
        
        for (size_t i = 0; i < grid_size; ++i) {
            double x = static_cast<double>(i);
            phi_prev[i] = 0.5 * sin(2.0 * PI * x / 20.0) + 0.3 * sin(2.0 * PI * x / 7.0) + 0.2 * cos(2.0 * PI * x / 13.0);
            phi_curr[i] = 0.6 * sin(2.0 * PI * x / 20.0 + 0.1) + 0.25 * sin(2.0 * PI * x / 7.0 + 0.05) + 0.15 * cos(2.0 * PI * x / 13.0 - 0.1);
            if (i > 0 && i < grid_size - 1 && i % 15 == 0) {
                source[i] = 0.5 * (1.0 + sin(x * 0.1));
            }
        }
        
        std::vector<double> phi_next = leap_frog_step(phi_prev, phi_curr, c, dt, dx, source);
        
        double max_val = *std::max_element(phi_next.begin(), phi_next.end(), [](double a, double b) {
            return std::abs(a) < std::abs(b);
        });
        double min_val = *std::min_element(phi_next.begin(), phi_next.end(), [](double a, double b) {
            return std::abs(a) < std::abs(b);
        });
        
        bool valid = true;
        for (size_t i = 0; i < phi_next.size(); ++i) {
            if (std::isnan(phi_next[i]) || std::isinf(phi_next[i])) {
                valid = false;
                break;
            }
        }
        
        bool stable = valid && std::abs(max_val) < 100.0 && std::abs(min_val) < 100.0;
        test1_passed = (phi_next.size() == phi_curr.size() && valid && stable);
        std::cout << "Test 1 : " << (test1_passed ? "correct" : "wrong") << "\n";
    }
    
    {
        double c = 2.5, dt = 0.001, dx = 0.01;
        double cfl = calculate_cfl(c, dt, dx);
        
        size_t grid_size = 500;
        std::vector<double> phi_prev(grid_size, 0.0);
        std::vector<double> phi_curr(grid_size, 0.0);
        std::vector<double> source(grid_size, 0.0);
        
        for (size_t i = 0; i < grid_size; ++i) {
            double x = static_cast<double>(i) * dx;
            if (x < 2.0) {
                phi_prev[i] = 10.0;
                phi_curr[i] = 10.0;
            } else if (x < 2.5) {
                phi_prev[i] = 10.0 * (1.0 - (x - 2.0) / 0.5) + 0.1 * sin(100.0 * PI * (x - 2.0));
                phi_curr[i] = 10.0 * (1.0 - (x - 2.0) / 0.5) + 0.15 * sin(100.0 * PI * (x - 2.0) + 0.1);
            } else {
                phi_prev[i] = 0.1 * sin(20.0 * PI * x);
                phi_curr[i] = 0.12 * sin(20.0 * PI * x + 0.05);
            }
            source[i] = 0.01 * sin(50.0 * PI * x) * cos(30.0 * PI * x);
        }
        
        std::vector<double> phi_next = leap_frog_step(phi_prev, phi_curr, c, dt, dx, source);
        
        double max_gradient = 0.0;
        for (size_t i = 1; i < phi_next.size() - 1; ++i) {
            double grad = std::abs(phi_next[i+1] - phi_next[i-1]) / (2.0 * dx);
            if (grad > max_gradient) max_gradient = grad;
        }
        
        bool valid = true;
        for (size_t i = 0; i < phi_next.size(); ++i) {
            if (std::isnan(phi_next[i]) || std::isinf(phi_next[i])) {
                valid = false;
                break;
            }
        }
        
        test2_passed = (phi_next.size() == phi_curr.size() && valid && max_gradient < 1e6);
        std::cout << "Test 2 : " << (test2_passed ? "correct" : "wrong") << "\n";
    }
    
    {
        double c = -1.75, dt = 0.15, dx = 0.25;
        double cfl = calculate_cfl(c, dt, dx);
        
        size_t grid_size = 300;
        std::vector<double> phi_prev(grid_size, 0.0);
        std::vector<double> phi_curr(grid_size, 0.0);
        std::vector<double> source(grid_size, 0.0);
        
        for (size_t i = 0; i < grid_size; ++i) {
            double x = static_cast<double>(i) * dx;
            phi_prev[i] = 5.0 * exp(-(x - 10.0) * (x - 10.0) / 2.0) + 
                          3.0 * exp(-(x - 30.0) * (x - 30.0) / 1.5) +
                          2.0 * exp(-(x - 50.0) * (x - 50.0) / 3.0);
            phi_curr[i] = 5.2 * exp(-(x - 10.5) * (x - 10.5) / 2.0) + 
                          3.1 * exp(-(x - 30.3) * (x - 30.3) / 1.5) +
                          2.1 * exp(-(x - 50.2) * (x - 50.2) / 3.0);
            if (i % 25 == 0 && i > 0 && i < grid_size - 1) {
                source[i] = 2.5 * (1.0 + 0.5 * sin(static_cast<double>(i) * 0.1));
            }
            source[i] += 0.05 * sin(5.0 * PI * x / 10.0) * exp(-x * x / 200.0);
        }
        
        std::vector<double> phi_next = leap_frog_step(phi_prev, phi_curr, c, dt, dx, source);
        
        bool valid = true;
        for (size_t i = 0; i < phi_next.size(); ++i) {
            if (std::isnan(phi_next[i]) || std::isinf(phi_next[i])) {
                valid = false;
                break;
            }
        }
        
        double max_val = *std::max_element(phi_next.begin(), phi_next.end(), [](double a, double b) {
            return std::abs(a) < std::abs(b);
        });
        bool reasonable = std::abs(max_val) < 1e3;
        
        test3_passed = (phi_next.size() == phi_curr.size() && valid && reasonable);
        std::cout << "Test 3 : " << (test3_passed ? "correct" : "wrong") << "\n";
    }
    
    return 0;
}

step_number: 2
step_description_prompt: Please write C++ code to implement a function named subproblem_9_2_functions. This function provides utilities for 1D advection equation solving, including two numerical time stepping schemes, boundary condition handling, source and initial condition generation, CFL calculation, adaptive time stepping logic, complete solving workflow, and returns a struct holding function pointers of all above utilities.
function_header: auto subproblem_9_2_functions() {
    struct Result
test_cases: int main() {
    unsigned int seed = 12345;
    srand(seed);
    
    auto funcs = subproblem_9_2_functions();
    auto apply_boundary_conditions = funcs.apply_boundary_conditions;
    auto create_source = funcs.create_source;
    
    bool test1_passed = false, test2_passed = false, test3_passed = false;
    
    {
        size_t grid_size = 500;
        std::vector<double> phi(grid_size, 0.0);
        
        for (size_t i = 0; i < grid_size; ++i) {
            double x = static_cast<double>(i);
            phi[i] = 10.0 * sin(2.0 * PI * x / 50.0) * exp(-x * x / 10000.0) + 
                     5.0 * cos(2.0 * PI * x / 17.0) * (1.0 + 0.5 * sin(2.0 * PI * x / 7.0));
        }
        
        apply_boundary_conditions(phi, "zero");
        
        std::vector<double> source = create_source(grid_size, "point", 100);
        source[200] = 2.5;
        source[300] = 1.8;
        source[400] = 3.2;
        source[150] = 1.5;
        source[350] = 2.1;
        
        bool bc_correct = (std::abs(phi[0]) < 1e-10) && (std::abs(phi[phi.size()-1]) < 1e-10);
        bool sources_correct = (source[100] == 1.0) && (source[200] == 2.5) && 
                               (source[300] == 1.8) && (source[400] == 3.2) &&
                               (std::abs(source[250]) < 1e-10);
        
        test1_passed = bc_correct && sources_correct;
        std::cout << "Test 1 : " << (test1_passed ? "correct" : "wrong") << "\n";
    }
    
    {
        size_t grid_size = 1000;
        std::vector<double> phi(grid_size, 0.0);
        
        for (size_t i = 0; i < grid_size; ++i) {
            double x = static_cast<double>(i);
            phi[i] = 15.0 * sin(2.0 * PI * x / 100.0) + 
                     8.0 * sin(2.0 * PI * x / 37.0) * cos(2.0 * PI * x / 23.0) +
                     5.0 * exp(-(x - 500.0) * (x - 500.0) / 5000.0) * sin(2.0 * PI * x / 13.0);
        }
        
        double phi_1_before = phi[1];
        double phi_n2_before = phi[grid_size-2];
        
        apply_boundary_conditions(phi, "periodic");
        
        std::vector<double> source = create_source(grid_size, "spacetime", 0);
        for (size_t i = 0; i < grid_size; ++i) {
            double x = static_cast<double>(i);
            source[i] += 0.2 * sin(2.0 * PI * x / 50.0) * cos(2.0 * PI * x / 31.0) +
                         0.15 * sin(2.0 * PI * x / 19.0) * exp(-x * x / 200000.0) +
                         0.1 * cos(2.0 * PI * x / 11.0) * (1.0 + 0.3 * sin(2.0 * PI * x / 7.0));
        }
        
        bool periodic_correct = (std::abs(phi[0] - phi_n2_before) < 1e-10) && 
                                (std::abs(phi[grid_size-1] - phi_1_before) < 1e-10);
        bool source_correct = (source.size() == grid_size) && 
                             (std::abs(source[0]) > 1e-10) && 
                             (std::abs(*std::max_element(source.begin(), source.end())) > 0.1);
        
        test2_passed = periodic_correct && source_correct;
        std::cout << "Test 2 : " << (test2_passed ? "correct" : "wrong") << "\n";
    }
    
    {
        size_t grid_size = 800;
        std::vector<double> phi(grid_size, 0.0);
        
        for (size_t i = 0; i < grid_size; ++i) {
            double x = static_cast<double>(i);
            if (i < 50) {
                phi[i] = 20.0 * (1.0 - exp(-x / 5.0)) * sin(10.0 * PI * x / 50.0);
            } else if (i >= grid_size - 50) {
                double x_rel = static_cast<double>(grid_size - 1 - i);
                phi[i] = 18.0 * (1.0 - exp(-x_rel / 5.0)) * cos(10.0 * PI * x_rel / 50.0);
            } else {
                phi[i] = 12.0 * sin(2.0 * PI * x / 80.0) * exp(-(x - 400.0) * (x - 400.0) / 8000.0) +
                         6.0 * cos(2.0 * PI * x / 29.0) * (1.0 + 0.4 * sin(2.0 * PI * x / 11.0));
            }
        }
        
        apply_boundary_conditions(phi, "reflective");
        
        std::vector<double> source = create_source(grid_size, "spacetime", 0);
        source[100] = 1.5;
        source[200] = 2.0;
        source[400] = 1.8;
        source[600] = 2.3;
        for (size_t i = 0; i < grid_size; ++i) {
            double x = static_cast<double>(i);
            source[i] += 0.1 * sin(2.0 * PI * x / 40.0) * (1.0 + 0.2 * cos(2.0 * PI * x / 17.0));
        }
        
        bool reflective_correct = (std::abs(phi[0] - phi[1]) < 1e-10) &&
                                  (std::abs(phi[grid_size-1] - phi[grid_size-2]) < 1e-10);
        bool source_correct = (source.size() == grid_size) && 
                             (std::abs(source[100] - 1.5) < 1e-10) &&
                             (std::abs(source[200] - 2.0) < 1e-10) &&
                             (std::abs(source[400] - 1.8) < 1e-10) &&
                             (std::abs(source[600] - 2.3) < 1e-10);
        
        test3_passed = reflective_correct && source_correct;
        std::cout << "Test 3 : " << (test3_passed ? "correct" : "wrong") << "\n";
    }
    
    return 0;
}

step_number: 3
step_description_prompt: Please write C++ code to implement a function named subproblem_9_3_functions. It returns a struct holding function pointers for advection equation solvers (leapfrog, Lax-Wendroff), boundary processing, CFL calculation, stability check, error analysis and benchmark verification utilities for numerical simulations.
function_header: auto subproblem_9_3_functions() {
    struct Result
test_cases: int main() {
    unsigned int seed = 12345;
    srand(seed);
    
    auto funcs = subproblem_9_3_functions();
    auto initialize_condition = funcs.initialize_condition;
    auto solve_complete = funcs.solve_complete;
    auto calculate_cfl = funcs.calculate_cfl;
    
    bool test1_passed = false, test2_passed = false, test3_passed = false;
    
    {
        std::vector<double> x;
        double x_min = -50.0, x_max = 50.0, dx = 0.1;
        for (double xi = x_min; xi <= x_max + dx/2.0; xi += dx) {
            x.push_back(xi);
        }
        
        std::vector<double> phi0(x.size(), 0.0);
        for (size_t i = 0; i < x.size(); ++i) {
            double xi = x[i];
            phi0[i] = 8.0 * exp(-(xi + 20.0) * (xi + 20.0) / 4.0) * sin(2.0 * PI * xi / 5.0) +
                      6.0 * exp(-(xi - 10.0) * (xi - 10.0) / 6.0) * cos(2.0 * PI * xi / 7.0) +
                      4.0 * exp(-(xi - 30.0) * (xi - 30.0) / 8.0) * (1.0 + 0.5 * sin(2.0 * PI * xi / 11.0));
            if (xi >= -5.0 && xi <= 5.0) {
                phi0[i] += 5.0;
            }
        }
        
        double c = 0.95, T = 20.0, dx_used = dx;
        double dt_approx = dx_used / (2.0 * std::abs(c));
        double cfl = calculate_cfl(c, dt_approx, dx_used);
        
        std::vector<std::vector<double>> results = solve_complete(phi0, c, T, dx_used, "zero", "leap_frog");
        
        bool valid = results.size() > 0 && results[0].size() == phi0.size();
        if (valid && results.size() > 0) {
            double max_final = *std::max_element(results.back().begin(), results.back().end(), [](double a, double b) {
                return std::abs(a) < std::abs(b);
            });
            valid = valid && std::abs(max_final) < 1e3;
        }
        
        test1_passed = valid;
        std::cout << "Test 1 : " << (test1_passed ? "correct" : "wrong") << "\n";
    }
    
    {
        std::vector<double> x;
        double x_min = -10.0, x_max = 10.0, dx = 0.02;
        for (double xi = x_min; xi <= x_max + dx/2.0; xi += dx) {
            x.push_back(xi);
        }
        
        std::vector<double> phi0(x.size(), 0.0);
        for (size_t i = 0; i < x.size(); ++i) {
            double xi = x[i];
            phi0[i] = 10.0 * exp(-xi * xi / 2.0);
            phi0[i] += 3.0 * exp(-(xi + 3.0) * (xi + 3.0) / 1.0);
            phi0[i] += 3.0 * exp(-(xi - 3.0) * (xi - 3.0) / 1.0);
            phi0[i] *= (1.0 + 0.1 * sin(20.0 * PI * xi / (x_max - x_min)));
        }
        
        double c = -1.2, T = 5.0, dx_used = dx;
        double dt_approx = dx_used / (2.0 * std::abs(c));
        double cfl = calculate_cfl(c, dt_approx, dx_used);
        
        std::vector<std::vector<double>> results = solve_complete(phi0, c, T, dx_used, "periodic", "lax_wendroff");
        
        bool valid = results.size() > 0 && results[0].size() == phi0.size();
        if (valid && results.size() > 1) {
            double max_final = *std::max_element(results.back().begin(), results.back().end(), [](double a, double b) {
                return std::abs(a) < std::abs(b);
            });
            valid = valid && std::abs(max_final) < 1e2;
        }
        
        test2_passed = valid;
        std::cout << "Test 2 : " << (test2_passed ? "correct" : "wrong") << "\n";
    }
    
    {
        std::vector<double> x;
        double x_min = -20.0, x_max = 20.0, dx = 0.25;
        for (double xi = x_min; xi <= x_max + dx/2.0; xi += dx) {
            x.push_back(xi);
        }
        
        std::vector<double> phi0(x.size(), 0.0);
        for (size_t i = 0; i < x.size(); ++i) {
            double xi = x[i];
            if (xi < 0.0) {
                phi0[i] = 15.0;
            } else if (xi < 2.0) {
                phi0[i] = 15.0 * (1.0 - xi / 2.0) + 2.0 * sin(50.0 * PI * xi) * exp(-xi * xi / 0.5);
            } else {
                phi0[i] = 0.5 * sin(10.0 * PI * xi / (x_max - x_min));
            }
            phi0[i] += 0.3 * sin(100.0 * PI * xi / (x_max - x_min)) * exp(-xi * xi / 20.0);
        }
        
        double c = 3.5, T = 2.5, dx_used = dx;
        double dt_approx = dx_used / (2.0 * std::abs(c));
        double cfl = calculate_cfl(c, dt_approx, dx_used);
        
        std::vector<std::vector<double>> results = solve_complete(phi0, c, T, dx_used, "reflective", "leap_frog");
        
        bool valid = results.size() > 0 && results[0].size() == phi0.size();
        if (valid && results.size() > 0) {
            double max_final = *std::max_element(results.back().begin(), results.back().end(), [](double a, double b) {
                return std::abs(a) < std::abs(b);
            });
            double min_final = *std::min_element(results.back().begin(), results.back().end(), [](double a, double b) {
                return std::abs(a) < std::abs(b);
            });
            valid = valid && std::abs(max_final) < 1e4 && std::abs(min_final) < 1e4;
            for (double val : results.back()) {
                if (std::isnan(val) || std::isinf(val)) {
                    valid = false;
                    break;
                }
            }
        }
        
        test3_passed = valid;
        std::cout << "Test 3 : " << (test3_passed ? "correct" : "wrong") << "\n";
    }
    
    return 0;
}

General solution:

General tests: