diff --git a/libnufi_lib.a b/libnufi_lib.a index ef5bc58..0b5fb6c 100644 Binary files a/libnufi_lib.a and b/libnufi_lib.a differ diff --git a/nufi/fields.h b/nufi/fields.h index 86f782d..81b4b7f 100644 --- a/nufi/fields.h +++ b/nufi/fields.h @@ -142,6 +142,41 @@ void interpolate( real *coeffs, const real *values) coeffs[ i ] = tmp[ i % Parameters::SPLINE_NX ]; } +class Gradient { +public: + Gradient(double xmin, double xmax, unsigned int Nx) + : xmin_(xmin), xmax_(xmax), Nx_(Nx) + { + if (xmax_ <= xmin_) { + throw std::invalid_argument("xmax must be greater than xmin"); + } + } + + std::vector compute(const std::vector& values) const { + size_t n = values.size(); + if (n < 2) { + throw std::invalid_argument("Need at least 2 points"); + } + + std::vector grad(n); + + double dx = (xmax_ - xmin_) / (n-1); + // periodic boundaries + grad[0] = -(values[1] - values[n-1]) / (2.0 * dx); + grad[n-1] = -(values[0] - values[n-2]) / (2.0 * dx); + + for (size_t i = 1; i < n-1; ++i) { + grad[i] = -(values[i+1] - values[i-1]) / (2.0 * dx); + } + + + return grad; + } +private: + double xmin_; + double xmax_; + [[maybe_unused]] unsigned int Nx_; +}; template class ChargeDensity : public Function // only uses f0 diff --git a/nufi/nufi_solver.h b/nufi/nufi_solver.h index 756cf2b..3d8669a 100644 --- a/nufi/nufi_solver.h +++ b/nufi/nufi_solver.h @@ -1,6 +1,7 @@ #ifndef NUFI_SOLVER_H #define NUFI_SOLVER_H +#include #include #include #include @@ -30,6 +31,9 @@ private: double Lx = Parameters::LX; + double x_min = Parameters::X_DOMAIN_LEFT; + double x_max = Parameters::X_DOMAIN_RIGHT; + std::vector rho; unsigned int order; diff --git a/nufi/poisson_problem.h b/nufi/poisson_problem.h index 068881d..7571603 100644 --- a/nufi/poisson_problem.h +++ b/nufi/poisson_problem.h @@ -3,6 +3,8 @@ #include +#include +#include #include #include #include @@ -34,6 +36,7 @@ #include #include +#include #include #include #include @@ -61,9 +64,8 @@ public: const Vector &get_solution() const { return solution; } const DoFHandler &get_dof_handler() const { return dof_handler; } - std::vector sample_electric_field(unsigned int Nx, - double x_min, - double x_max); + std::vector sample_electric_field(double x_min, double x_max, unsigned int Nx); + std::vector sample_electric_potential(double x_min, double x_max, unsigned int Nx); private: void create_mesh(); @@ -104,33 +106,71 @@ PoissonProblem::PoissonProblem(unsigned int degree) {} template -std::vector PoissonProblem::sample_electric_field( - unsigned int Nx, - double x_min, - double x_max) +std::vector PoissonProblem::sample_electric_field(double x_min,double x_max,unsigned int Nx) { - this -> get_solution(); - this -> get_dof_handler(); + std::vector E_values(Nx); - Functions::FEFieldFunction> - field_function(dof_handler, solution, mapping); + const double dx = (x_max - x_min) / (Nx - 1); + for (unsigned int i = 0; i < Nx; ++i) + { + const double x = x_min + i * dx; + const Point point(x); + + // 1. Find the active cell containing x + const auto cell_point_pair = + GridTools::find_active_cell_around_point(mapping, + dof_handler, + point); + + const auto cell = cell_point_pair.first; + const Point &unit_point = cell_point_pair.second; + + // 2. FEPointEvaluation expects an ArrayView of points + std::vector> points(1, unit_point); + ArrayView> point_view(points); + + FEPointEvaluation<1, dim> evaluator(mapping, + dof_handler.get_fe(), + update_gradients); + + // reinit with ArrayView of points + evaluator.reinit(cell, point_view); + + Vector local_dofs(dof_handler.get_fe().dofs_per_cell); + cell->get_dof_values(solution, local_dofs); + + // 3. Evaluate gradient at this point + evaluator.evaluate(local_dofs, EvaluationFlags::gradients); + + const Tensor<1, dim> grad_phi = evaluator.get_gradient(0); + + // 4. Compute E = -grad(phi) + E_values[i] = -grad_phi[0]; + } + + return E_values; +} + +template +std::vector PoissonProblem::sample_electric_potential( + double x_min, + double x_max, + unsigned int Nx) +{ std::vector values(Nx); + std::vector> eval_points(Nx); double Lx = x_max - x_min; double dx = Lx / Nx; - for (unsigned int i = 0; i < Nx; ++i) - { - double x = x_min + i * dx; + for(unsigned int i=0 ; i(x_min + i * dx); - Point p; - p[0] = x; + Utilities::MPI::RemotePointEvaluation cache; + cache.reinit(eval_points, triangulation, mapping); - Tensor<1, dim> grad = field_function.gradient(p); - - values[i] = -grad[0]; // E = -dφ/dx - } + values = VectorTools::point_values(cache, dof_handler, solution); return values; } diff --git a/nufi/save_results.h b/nufi/save_results.h index 8e6258b..e1a3247 100644 --- a/nufi/save_results.h +++ b/nufi/save_results.h @@ -23,4 +23,7 @@ void save_Efield(unsigned int n, unsigned int Nx_out, const std::string &filename); + +void save_space_vector(const std::vector& vals, const std::string& filename, size_t it); + #endif diff --git a/rho_E.mp4 b/rho_E.mp4 index 0e7944e..0f4af77 100644 Binary files a/rho_E.mp4 and b/rho_E.mp4 differ diff --git a/src/nufi_solver.cc b/src/nufi_solver.cc index 2bb11e0..de4b9a0 100644 --- a/src/nufi_solver.cc +++ b/src/nufi_solver.cc @@ -28,7 +28,7 @@ double NuFISolver::eval_ftilda(unsigned int n, if ( n == 0 ) return f0(x,u); const size_t stride_x = 1; - const size_t stride_t = stride_x*(Parameters::SPLINE_NX + Parameters::SPLINE_ORDER - 1); + const size_t stride_t = stride_x*(Nx + Parameters::SPLINE_ORDER - 1); double Ex; const double *c; @@ -81,6 +81,8 @@ void NuFISolver::run() if ( rho == nullptr ) throw std::bad_alloc {}; + Gradient grad(x_min, x_max, Nx); + for (unsigned int it = 0; it < Nt; ++it) { std::cout << "Timestep " << it << " / " << Nt << std::endl << std::endl; @@ -97,10 +99,17 @@ void NuFISolver::run() rho.get()[i] = ith_rho; } - poisson.set_rhs_function(std::make_unique>(rho.get(), Parameters::SPLINE_NX)); + poisson.set_rhs_function(std::make_unique>(rho.get(), Nx)); poisson.solve_step(); - std::vector E_vals = poisson.sample_electric_field(Parameters::SPLINE_NX, Parameters::X_DOMAIN_LEFT, Parameters::X_DOMAIN_RIGHT); + std::vector sampled_potential = poisson.sample_electric_potential(x_min, x_max, Nx); + + save_space_vector(sampled_potential, "potential", it); + + std::vector E_vals = grad.compute(sampled_potential); + // std::vector E_vals = poisson.sample_electric_field(x_min, x_max, Nx); + + save_space_vector(E_vals, "electric", it); double* current_coeffs = coeffs.get() + it*stride_t; interpolate(current_coeffs, E_vals.data()); diff --git a/src/save_results.cc b/src/save_results.cc index 8654d66..dfb8f7d 100644 --- a/src/save_results.cc +++ b/src/save_results.cc @@ -2,7 +2,9 @@ #include "nufi/parameters.h" #include +#include #include +#include #include "nufi/nufi_solver.h" @@ -102,3 +104,15 @@ void save_Efield(unsigned int n, } file.close(); } + +void save_space_vector(const std::vector& vals, const std::string& filename, size_t it) +{ + std::ofstream file("results/" + filename + "_" + std::to_string(it) + ".dat"); + + if (!file) throw std::runtime_error("failed to start file in results/"); + + file << vals.size() << "\n"; + file << Parameters::X_DOMAIN_LEFT << " " << Parameters::X_DOMAIN_RIGHT << "\n"; + file << std::fixed << std::setprecision(8); + for (double val : vals) file << val << "\n"; +}