Hello all, I am unable to edit my original post. So I am creating a new post with the equations. I think that I solved issue with the equations not being displayed. The original posting will be deleted.
I have recently started to learn how to use Deal.II. I have been spending some going through some of the video tutorials for Deal.II, going through the coding tutorials, and watching some of the videos from the University of Michigan related to Finite Element Analysis. I have decided that I need to get my hands a bit dirty so that everything that I am reading and watching will start to sink. So please, bear with me. I am still quite new to this field and I am still learning some of the basics. With that being said, I am looking at creating my own electrostatic solver. For those unfamiliar, I am solving the PDEs: For the electrostatic simulation, I would be solving for the Voltage scalar field and the Electric field (which is a vector). So far, I have found that there are two paths that I can take to get my bilinear form. I have already attempted one path and I ran into a compile error with Deal.II. So, I am thinking that this bilinear form may not be appropriate. But I wanted to turn to the community here to see if my bilinear form has an issue or if my code has an issue. For the first path, since V and E are my unknowns, I wanted both V and E to be on the left-hand side of their PDEs. With a bit of substitution, the equations that I would be solving turned into this: Normally, there would be a - signed on the right-hand side of both equations. This will get dropped out because I am going to simplify the equation a bit further later down the road. In order to construct the bilinear form, I used the technique introduced in lecture 19. I am not going to show the entire process but rather the end result. If the entire process is needed, please post and I will respond with the complete step-by-step process that I performed. then This is how I created my bilinear form. Note that the p/e term dropped out because in this simplified simulation, there is no charge density. I create my simulation that is based on the tutorial step-20. There are alot of extra features in the tutorial that I did not add in to my simulation but rather focused on the FESystem portion. My solver is a CG Solver. Nothing fancy here. My first version of the solver can be found below void SetupSolver(FESystem<2> &finiteElement, DoFHandler<2> &dofHandler, SparseMatrix<double> &masterMatrix, Vector<double> &masterRHS, Vector <double> &masterSolution) { QGauss<2> quadrature(3);// Sets the form of the function FEValues<2> finiteElementValues(finiteElement, quadrature, update_values | update_gradients | update_JxW_values | update_quadrature_points); const unsigned int dofsPerCell = finiteElement.dofs_per_cell; const unsigned int numberQuadraturePoints = quadrature.size(); FullMatrix<double> cell_matrix(dofsPerCell, dofsPerCell); Vector<double> rhsCell(dofsPerCell); vector<types::global_dof_index> localDofIndices(dofsPerCell); DoFHandler<2>::active_cell_iterator cell = dofHandler.begin_active(); DoFHandler<2>::active_cell_iterator endc = dofHandler.end(); const FEValuesExtractors::Vector electricField(2); const FEValuesExtractors::Scalar voltageField(0); cout << "Setting up Solver" << endl; for(; cell != endc; cell++) { // Calculates the values and gradients of the shape function for a particular cell finiteElementValues.reinit(cell); cell_matrix = 0; rhsCell = 0; cout << "working on cell: " << cell << endl; for(unsigned int qIndex = 0; qIndex < numberQuadraturePoints; qIndex ++) { for(unsigned int i = 0; i < dofsPerCell; i++) { const double div_phi_i_e = finiteElementValues[electricField ].divergence(i, qIndex); const Tensor<1, 2> grad_phi_i_v = finiteElementValues[ voltageField].gradient(i, qIndex); for(unsigned int j = 0; j < dofsPerCell; j++) { const Tensor<1, 2> phi_j_e = finiteElementValues[ electricField].value(j, qIndex); const Tensor<1, 2> grad_phi_j_v = finiteElementValues[ voltageField].gradient(j, qIndex); cell_matrix(i, j) += ((div_phi_i_e * phi_j_e) + (grad_phi_i_v * grad_phi_j_v)) * finiteElementValues.JxW(qIndex); } rhsCell(i) += 0.0; } } cell->get_dof_indices(localDofIndices); for(unsigned int i = 0; i < dofsPerCell; i++) { for(unsigned int j = 0; j < dofsPerCell; j++) { masterMatrix.add(localDofIndices[i], localDofIndices[j], cell_matrix(i, j)); } } for(unsigned int i = 0; i < dofsPerCell; i++) { masterRHS(localDofIndices[i]) += rhsCell(i); } } cout << "Finished setting up cells" << endl; // SolvingFunction test(); map<types::global_dof_index, double> boundaryValues; cout << "Interperlating boundary conditions" << endl; VectorTools::interpolate_boundary_values(dofHandler, 1, ConstantFunction <2>(100.), boundaryValues); VectorTools::interpolate_boundary_values(dofHandler, 2, ZeroFunction<2 >(), boundaryValues); VectorTools::interpolate_boundary_values(dofHandler, 0, ZeroFunction<2 >(), boundaryValues); MatrixTools::apply_boundary_values(boundaryValues, masterMatrix, masterSolution, masterRHS); cout << "Finished setting up solver" << endl; } On line cell_matrix(i, j) += ((div_phi_i_e * phi_j_e) + (grad_phi_i_v * grad_phi_j_v )) * finiteElementValues.JxW(qIndex); I am getting the error: error: no match for 'operator+' (operand types are 'dealii::Tensor<1, 2>' and 'dealii::Tensor<0, 2, double>::tensor_type {aka double}') Which, from what the error is stating, the + operator can not take these two arguments. This then leads me to reason that my bilinear form has an issue. Would this be a correct assumption? Or did I code something incorrectly? (This is my main issue right now) Path 2: I have also been considering this approach for creating the bilinear form where the PDEs that I want to solve are as follows: Again, following the technique introduced in Lecture 19, I would simplify my PDEs into: then I was wondering if this would be a better approach in constructing the bi-linear form and solving for the electric field and voltage field. Or are both methods equally valid? I have a feeling that the first method is not valid since it is essentially 2 of the same equations. What are your thoughts? Is there a different method that I can approach in solving the PDEs for the electric and voltage fields? Side Notes: There is a third equation which is the curl of the electric field is 0 for electrostatics. I decided that I am not using it since these two are enough. Is this an incorrect thinking and should I also account for this equation in my bilinear form? I am using Deal.II to create a mesh which is a parallel plate capacitor. I am then solving for the electric and voltage fields that make up the parallel plates when the voltage and electric fields are 0 on the boundary. On the boundary of one of the plates, the Voltage is set to 100. On the other plate, the Voltage is set to 0. For reference, I am attaching the source code of my simulation. There are more improvements that I will be making to the code once I get something workable. The code that is attached is a modified version of an earlier simulation where I was only solving for the voltage field in a parallel plate. This was around 2 years ago that I created this. I am simply expanding the code to also solve for the electric field. in short, it is rough. If you do see any other mistakes or areas of improvement, feel free to let me know. Thank you -- The deal.II project is located at http://www.dealii.org/ For mailing list/forum options, see https://groups.google.com/d/forum/dealii?hl=en --- You received this message because you are subscribed to the Google Groups "deal.II User Group" group. To unsubscribe from this group and stop receiving emails from it, send an email to dealii+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
/* This file is the previous version of the capacitor simulator. This builds and does simulate! */ #include <deal.II/grid/tria.h> #include <deal.II/grid/tria_accessor.h> #include <deal.II/grid/tria_iterator.h> #include <deal.II/grid/grid_generator.h> #include <deal.II/grid/tria_boundary_lib.h> #include <deal.II/grid/grid_out.h> #include <deal.II/grid/manifold_lib.h> #include <deal.II/fe/fe_q.h> #include <deal.II/fe/fe_system.h> #include <deal.II/fe/fe_values.h> #include <deal.II/fe/fe_values_extractors.h> #include <deal.II/dofs/dof_handler.h> #include <deal.II/dofs/dof_renumbering.h> #include <deal.II/dofs/dof_tools.h> #include <deal.II/lac/sparse_matrix.h> #include <deal.II/lac/dynamic_sparsity_pattern.h> #include <deal.II/lac/vector.h> #include <deal.II/lac/solver_cg.h> #include <deal.II/lac/precondition.h> #include <deal.II/numerics/vector_tools.h> #include <deal.II/numerics/matrix_tools.h> #include <deal.II/numerics/data_out.h> #include <fstream> #include <cmath> #include <set> using namespace dealii; using namespace std; using namespace GridGenerator; // Generates the grid needed void generateGrid(unsigned int radius, double distance, double height, double length, Triangulation<2> &finalProblem) { const Point<2> centerBoundary (0,0); const SphericalManifold<2> manifold_description(centerBoundary); set<Triangulation<2>::active_cell_iterator> cellsToDelete; Triangulation<2>::active_cell_iterator cell; Triangulation<2>::active_cell_iterator endCell; GridOut outputGrid; // This is the Triangulation of the entire problem Triangulation<2> parallelPlate; hyper_ball(parallelPlate, centerBoundary, radius); cout << "Creating Grid" << endl; if(length > distance) { cout << "This will NOT work"; return; } parallelPlate.set_all_manifold_ids(0); for (auto cell : parallelPlate.active_cell_iterators()) { if (cell->center().square() < 1.e-10) { cell->set_manifold_id(numbers::invalid_manifold_id); for(unsigned int i = 0; i < GeometryInfo<2>::faces_per_cell; ++i) { cell->face(i)->set_manifold_id(numbers::invalid_manifold_id); } } } cell = parallelPlate.begin_active(); endCell = parallelPlate.end(); parallelPlate.set_manifold(0, manifold_description); parallelPlate.refine_global(4); //TODO: add in flag for smoothing-> Triangulation::MeshSmoothing // This will interate over all of the cells and delete the cells to make the two parallel plates cout << "Creating parallel Plates" << endl; // Iterates through all of the cells for(;cell != endCell; cell++) { for(unsigned int v = 0; v < 4; v++) { if(abs(cell->vertex(v)(1)) <= (height / 2) && abs(cell->vertex(v)(0)) >= (distance / 2) && abs(cell->vertex(v)(0)) <= (distance / 2 + length)) { // Marks the cells that fit the if statement for deletion cellsToDelete.insert(cell); break; } } } parallelPlate.set_manifold(0); GridGenerator::create_triangulation_with_removed_cells(parallelPlate, cellsToDelete, finalProblem); // Reset the cell and endCell interators cell = finalProblem.begin_active(); endCell = finalProblem.end(); // This will interrate over all of the cells and refine the ones that are within the boundaries of the plates within the air cout << "Refining grid around plates" << endl; for(int step = 0; step < 2; step++) { // Iterates through all of the cells for(;cell != endCell; cell++) { for(unsigned int v = 0; v < 2; v++) { if(abs(cell->vertex(v)(1)) <= 4.4 && abs(cell->vertex(v)(0)) <= 4.4) { // Marks the cells that fit the if statement for refinement cell->set_refine_flag(); break; } } } cell = finalProblem.begin_active();// Resets the cell back to the beginning // Creates a new mesh with the refined cells finalProblem.execute_coarsening_and_refinement(); } cell = finalProblem.begin_active(); endCell = finalProblem.end(); // Iterate through all of the cells and determine which ones are at the boundary for the plates cout << "Determining Boundary of parallel Plates" << endl; for(; cell != endCell; cell++) { if(cell->at_boundary()) { for(unsigned int f = 0; f < GeometryInfo<2>::faces_per_cell; f++) { if(cell->face(f)->at_boundary()) { for(unsigned int v = 0; v < GeometryInfo<2>::vertices_per_face; v++) { // Change the boundary ID for the cell here // These two checks will ensure that only the cells related to the edge of the boundary for the parallel plates will be selected // and modified // The + 1 is added to ensure that the cells on the back side of the boundary are selected if(abs(cell->vertex(v)(0)) <= (distance / 2 + length + 1) && abs(cell->vertex(v)(1)) <= (height) && cell->vertex(v)(0) < 0) { // Sets the Boundary ID for the left plate cell->face(f)->set_boundary_id(1); } else if(abs(cell->vertex(v)(0)) <= (distance / 2 + length + 1) && abs(cell->vertex(v)(1)) <= height && cell->vertex(v)(0) > 0) { // Sets the Boundary ID for Right plate cell->face(f)->set_boundary_id(2); } } } } } } ofstream out ("plate.vtk"); outputGrid.write_vtk(finalProblem, out); cout << "Finish creating grid" << endl; } void GenerateDOFS(DoFHandler<2> &dofHandler, FESystem<2> &finiteElement, SparsityPattern &sparsePattern, SparseMatrix<double> &masterMatrix, Vector<double> &masterRHS, Vector<double> &masterSolution) { dofHandler.distribute_dofs(finiteElement); cout << "calculating degrees of freedom" << endl; // Creates a dynamic Sparsity Pattern so that the sparsity pattern function does not over estimate the number of elements DynamicSparsityPattern dynamicSparsityPattern(dofHandler.n_dofs(), dofHandler.n_dofs()); DoFTools::make_sparsity_pattern(dofHandler, dynamicSparsityPattern); sparsePattern.copy_from(dynamicSparsityPattern);// Copies the dynamic sparsity pattern into the sparse pattern object cout << "Number of degress of freedom: " << dofHandler.n_dofs() << endl; masterMatrix.reinit(sparsePattern);// Initilizes the masterMatrix with the sparse pattern masterRHS.reinit(dofHandler.n_dofs()); masterSolution.reinit(dofHandler.n_dofs()); } void SetupSolver(FESystem<2> &finiteElement, DoFHandler<2> &dofHandler, SparseMatrix<double> &masterMatrix, Vector<double> &masterRHS, Vector<double> &masterSolution) { QGauss<2> quadrature(3);// Sets the form of the function FEValues<2> finiteElementValues(finiteElement, quadrature, update_values | update_gradients | update_JxW_values | update_quadrature_points); const unsigned int dofsPerCell = finiteElement.dofs_per_cell; const unsigned int numberQuadraturePoints = quadrature.size(); FullMatrix<double> cell_matrix(dofsPerCell, dofsPerCell); Vector<double> rhsCell(dofsPerCell); vector<types::global_dof_index> localDofIndices(dofsPerCell); DoFHandler<2>::active_cell_iterator cell = dofHandler.begin_active(); DoFHandler<2>::active_cell_iterator endc = dofHandler.end(); const FEValuesExtractors::Vector electricField(2); const FEValuesExtractors::Scalar voltageField(0); cout << "Setting up Solver" << endl; for(; cell != endc; cell++) { // Calculates the values and gradients of the shape function for a particular cell finiteElementValues.reinit(cell); cell_matrix = 0; rhsCell = 0; cout << "working on cell: " << cell << endl; for(unsigned int qIndex = 0; qIndex < numberQuadraturePoints; qIndex++) { for(unsigned int i = 0; i < dofsPerCell; i++) { const double div_phi_i_e = finiteElementValues[electricField].divergence(i, qIndex); const Tensor<1, 2> grad_phi_i_v = finiteElementValues[voltageField].gradient(i, qIndex); for(unsigned int j = 0; j < dofsPerCell; j++) { const Tensor<1, 2> phi_j_e = finiteElementValues[electricField].value(j, qIndex); const Tensor<1, 2> grad_phi_j_v = finiteElementValues[voltageField].gradient(j, qIndex); cell_matrix(i, j) += ((div_phi_i_e * phi_j_e) + (grad_phi_i_v * grad_phi_j_v)) * finiteElementValues.JxW(qIndex); } rhsCell(i) += 0.0; } } cell->get_dof_indices(localDofIndices); for(unsigned int i = 0; i < dofsPerCell; i++) { for(unsigned int j = 0; j < dofsPerCell; j++) { masterMatrix.add(localDofIndices[i], localDofIndices[j], cell_matrix(i, j)); } } for(unsigned int i = 0; i < dofsPerCell; i++) { masterRHS(localDofIndices[i]) += rhsCell(i); } } cout << "Finished setting up cells" << endl; // SolvingFunction test(); map<types::global_dof_index, double> boundaryValues; cout << "Interperlating boundary conditions" << endl; VectorTools::interpolate_boundary_values(dofHandler, 1, ConstantFunction<2>(100.), boundaryValues); VectorTools::interpolate_boundary_values(dofHandler, 2, ZeroFunction<2>(), boundaryValues); VectorTools::interpolate_boundary_values(dofHandler, 0, ZeroFunction<2>(), boundaryValues); MatrixTools::apply_boundary_values(boundaryValues, masterMatrix, masterSolution, masterRHS); cout << "Finished setting up solver" << endl; } void solve(SparseMatrix<double> &masterMatrix, Vector<double> &masterSolution, Vector<double> &masterRHS) { SolverControl solverControl(100000, 1e-12); SolverCG<> solver(solverControl); cout << "Beginning solver" << endl; solver.solve(masterMatrix, masterSolution, masterRHS, PreconditionIdentity()); cout << "Finished solving" << endl; } void outputResults(DoFHandler<2> &masterDOFHandler, Vector<double> &masterSolution) { std::vector<std::string> solutionNames(2, "Electric Field"); solutionNames.push_back("Voltage Value"); std::vector<DataComponentInterpretation::DataComponentInterpretation> interpret(2, DataComponentInterpretation::component_is_part_of_vector); interpret.push_back(DataComponentInterpretation::component_is_scalar); DataOut<2> outputData; outputData.add_data_vector(masterDOFHandler, masterSolution, solutionNames, interpret); outputData.build_patches(); ofstream output("solution.vtk"); outputData.write_vtk(output); } int main() { Triangulation<2> testtri; FE_Q<2> finiteElementQuadrature(2); FESystem<2> electrostaticSystem; SparsityPattern sparsePattern; SparseMatrix<double> systemMatrix; Vector<double> solution; Vector<double> RHS; generateGrid(15, 2, 5, 0.5, testtri); DoFHandler<2> internalDOFHandler(testtri); GenerateDOFS(internalDOFHandler, electrostaticSystem, sparsePattern, systemMatrix, RHS, solution); SetupSolver(electrostaticSystem, internalDOFHandler, systemMatrix, RHS, solution); solve(systemMatrix, solution, RHS); outputResults(internalDOFHandler, solution); return 0; }