diff --git a/.clang-tidy b/.clang-tidy index 6919bf1b2..f7c32069a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -9,15 +9,19 @@ Checks: > cppcoreguidelines-*, -cppcoreguidelines-avoid-const-or-ref-data-members, -cppcoreguidelines-non-private-member-variables-in-classes, + -cppcoreguidelines-avoid-magic-numbers, hicpp-*, misc-*, -misc-non-private-member-variables-in-classes, modernize-*, -modernize-use-trailing-return-type, + -modernize-pass-by-value, mpi-*, performance-*, readability-*, -readability-else-after-return, -readability-identifier-length, + -readability-magic-numbers, + -readability-convert-member-functions-to-static, WarningsAsErrors: '*' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 48b3b3291..cf7bf9d70 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -7,7 +7,6 @@ on: pull_request: branches: - master - - devel types: - opened - reopened diff --git a/.gitignore b/.gitignore index 33d26c1cf..231a8aca6 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ main #Output files applications/*/*.vtk *.vtu +*.pvtu *.vts *.frt integratedFields.txt diff --git a/applications/main.cc b/applications/main.cc index f5526507c..c9fd00bdf 100644 --- a/applications/main.cc +++ b/applications/main.cc @@ -5,8 +5,8 @@ #include "core/variableAttributes.h" #include "equations.cc" -#include #include +#include #include // Header file for postprocessing that may or may not exist @@ -38,7 +38,7 @@ main(int argc, char **argv) std::string parameters_filename; try { - ParseCommandLineOpts cli_options(argc, argv); + parseCMDOptions cli_options(argc, argv); parameters_filename = cli_options.getParametersFilename(); } catch (const char *msg) diff --git a/applications/multigrid_test/CMakeLists.txt b/applications/multigrid_test/CMakeLists.txt new file mode 100644 index 000000000..c02e02537 --- /dev/null +++ b/applications/multigrid_test/CMakeLists.txt @@ -0,0 +1,33 @@ +## +# CMake script for the PRISMS-PF applications +# Adapted from the ASPECT CMake file +## + +cmake_minimum_required(VERSION 3.3.0) + +include(${CMAKE_SOURCE_DIR}/../../cmake/setup_application.cmake) + +project(myapp) + +# Set location of files +include_directories(${CMAKE_SOURCE_DIR}/../../include) +include_directories(${CMAKE_SOURCE_DIR}/../../src) +include_directories(${CMAKE_SOURCE_DIR}) + +# Set the location of the main.cc file +set(TARGET_SRC "${CMAKE_SOURCE_DIR}/main.cc" "${CMAKE_SOURCE_DIR}/equations.cc" "${CMAKE_SOURCE_DIR}/ICs_and_BCs.cc") + +# Set targets & link libraries for the build type +if(${PRISMS_PF_BUILD_DEBUG} STREQUAL "ON") + add_executable(main_debug ${TARGET_SRC}) + set_property(TARGET main_debug PROPERTY OUTPUT_NAME main-debug) + deal_ii_setup_target(main_debug DEBUG) + target_link_libraries(main_debug ${CMAKE_SOURCE_DIR}/../../libprisms-pf-debug.a) +endif() + +if(${PRISMS_PF_BUILD_RELEASE} STREQUAL "ON") + add_executable(main_release ${TARGET_SRC}) + set_property(TARGET main_release PROPERTY OUTPUT_NAME main) + deal_ii_setup_target(main_release RELEASE) + target_link_libraries(main_release ${CMAKE_SOURCE_DIR}/../../libprisms-pf-release.a) +endif() \ No newline at end of file diff --git a/applications/multigrid_test/ICs_and_BCs.cc b/applications/multigrid_test/ICs_and_BCs.cc new file mode 100644 index 000000000..cac330149 --- /dev/null +++ b/applications/multigrid_test/ICs_and_BCs.cc @@ -0,0 +1,24 @@ +#include +#include +#include + +template +void +customNonuniformDirichlet::set_nonuniform_dirichlet( + [[maybe_unused]] const uint &index, + [[maybe_unused]] const uint &boundary_id, + [[maybe_unused]] const uint &component, + [[maybe_unused]] const dealii::Point &point, + [[maybe_unused]] double &scalar_value, + [[maybe_unused]] double &vector_component_value) const +{ + if (index == 0) + { + if (boundary_id == 0 || boundary_id == 1) + { + scalar_value = std::sin(point[1] * M_PI); + } + } +} + +INSTANTIATE_UNI_TEMPLATE(customNonuniformDirichlet) \ No newline at end of file diff --git a/applications/multigrid_test/custom_pde.h b/applications/multigrid_test/custom_pde.h new file mode 100644 index 000000000..550ac9a42 --- /dev/null +++ b/applications/multigrid_test/custom_pde.h @@ -0,0 +1,68 @@ +#ifndef CUSTOM_PDE_H_ +#define CUSTOM_PDE_H_ + +#include +#include +#include + +using namespace dealii; + +/** + * \brief This is a derived class of `matrixFreeOperator` where the user implements their + * PDEs. + * + * \tparam dim The number of dimensions in the problem. + * \tparam degree The polynomial degree of the shape functions. + * \tparam number Datatype to use. Either double or float. + */ +template +class customPDE : public matrixFreeOperator +{ +public: + using scalarValue = dealii::VectorizedArray; + using scalarGrad = dealii::Tensor<1, dim, dealii::VectorizedArray>; + using scalarHess = dealii::Tensor<2, dim, dealii::VectorizedArray>; + using vectorValue = dealii::Tensor<1, dim, dealii::VectorizedArray>; + using vectorGrad = dealii::Tensor<2, dim, dealii::VectorizedArray>; + using vectorHess = dealii::Tensor<3, dim, dealii::VectorizedArray>; + + /** + * \brief Constructor. + */ + customPDE(const userInputParameters &_user_inputs) + : matrixFreeOperator(_user_inputs.var_attributes) + , user_inputs(_user_inputs) + {} + +private: + /** + * \brief User-implemented class for the RHS of explicit equations. + */ + void + compute_explicit_RHS(variableContainer &variable_list, + const dealii::Point> + &q_point_loc) const override; + + /** + * \brief User-implemented class for the RHS of nonexplicit equations. + */ + void + compute_nonexplicit_RHS(variableContainer &variable_list, + const dealii::Point> + &q_point_loc) const override; + + /** + * \brief User-implemented class for the LHS of nonexplicit equations. + */ + void + compute_nonexplicit_LHS(variableContainer &variable_list, + const dealii::Point> + &q_point_loc) const override; + + /** + * \brief The userinputs. + */ + const userInputParameters &user_inputs; +}; + +#endif \ No newline at end of file diff --git a/applications/multigrid_test/equations.cc b/applications/multigrid_test/equations.cc new file mode 100644 index 000000000..0e708d666 --- /dev/null +++ b/applications/multigrid_test/equations.cc @@ -0,0 +1,53 @@ +#include "custom_pde.h" + +#include +#include +#include + +void +customAttributeLoader::loadVariableAttributes() +{ + set_variable_name(0, "phi"); + set_variable_type(0, SCALAR); + set_variable_equation_type(0, TIME_INDEPENDENT); + + set_dependencies_value_term_RHS(0, ""); + set_dependencies_gradient_term_RHS(0, "grad(phi)"); + set_dependencies_value_term_LHS(0, ""); + set_dependencies_gradient_term_LHS(0, "grad(change(phi))"); +} + +void +customAttributeLoader::loadPostProcessorVariableAttributes() +{} + +template +void +customPDE::compute_explicit_RHS( + [[maybe_unused]] variableContainer &variable_list, + [[maybe_unused]] const Point> &q_point_loc) const +{} + +template +void +customPDE::compute_nonexplicit_RHS( + [[maybe_unused]] variableContainer &variable_list, + [[maybe_unused]] const Point> &q_point_loc) const +{ + scalarGrad phix = variable_list.get_scalar_gradient(0); + + variable_list.set_scalar_gradient_term(0, -phix); +} + +template +void +customPDE::compute_nonexplicit_LHS( + [[maybe_unused]] variableContainer &variable_list, + [[maybe_unused]] const Point> &q_point_loc) const +{ + scalarGrad change_phix = variable_list.get_scalar_gradient(0, CHANGE); + + variable_list.set_scalar_gradient_term(0, change_phix, CHANGE); +} + +INSTANTIATE_TRI_TEMPLATE(customPDE) \ No newline at end of file diff --git a/applications/multigrid_test/main.cc b/applications/multigrid_test/main.cc new file mode 100644 index 000000000..c1dfb496d --- /dev/null +++ b/applications/multigrid_test/main.cc @@ -0,0 +1,215 @@ +#include "custom_pde.h" + +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + try + { + Utilities::MPI::MPI_InitFinalize mpi_init(argc, argv, 1); + + // Parse the command line options (if there are any) to get the name of the input + // file + parseCMDOptions cli_options(argc, argv); + std::string parameters_filename = cli_options.getParametersFilename(); + + // Restrict deal.II console printing + dealii::deallog.depth_console(0); + + // Before fully parsing the parameter file, we need to know how many field + // variables there are and whether they are scalars or vectors, how many + // postprocessing variables there are, how many sets of elastic constants + // there are, and how many user-defined constants there are. + // + // This is done with the derived class of `variableAttributeLoader`, + // `customAttributeLoader`. + customAttributeLoader attribute_loader; + attribute_loader.init_variable_attributes(); + AttributesList var_attributes = attribute_loader.get_var_attributes(); + AttributesList pp_attributes = attribute_loader.get_pp_attributes(); + + // Load in parameters + inputFileReader input_file_reader(parameters_filename, + var_attributes, + pp_attributes); + + // Run problem based on the number of dimensions and element degree + switch (input_file_reader.number_of_dimensions) + { + case 1: + { + userInputParameters<1> user_inputs(input_file_reader, + input_file_reader.parameter_handler); + switch (user_inputs.spatial_discretization.degree) + { + case 1: + { + PDEProblem<1, 1> problem(user_inputs); + problem.run(); + break; + } + case 2: + { + PDEProblem<1, 2> problem(user_inputs); + problem.run(); + break; + } + case 3: + { + PDEProblem<1, 3> problem(user_inputs); + problem.run(); + break; + } + case 4: + { + PDEProblem<1, 4> problem(user_inputs); + problem.run(); + break; + } + case 5: + { + PDEProblem<1, 5> problem(user_inputs); + problem.run(); + break; + } + case 6: + { + PDEProblem<1, 6> problem(user_inputs); + problem.run(); + break; + } + default: + throw std::runtime_error("Invalid element degree"); + } + break; + } + case 2: + { + userInputParameters<2> user_inputs(input_file_reader, + input_file_reader.parameter_handler); + switch (user_inputs.spatial_discretization.degree) + { + case 1: + { + PDEProblem<2, 1> problem(user_inputs); + problem.run(); + break; + } + case 2: + { + PDEProblem<2, 2> problem(user_inputs); + problem.run(); + break; + } + case 3: + { + PDEProblem<2, 3> problem(user_inputs); + problem.run(); + break; + } + case 4: + { + PDEProblem<2, 4> problem(user_inputs); + problem.run(); + break; + } + case 5: + { + PDEProblem<2, 5> problem(user_inputs); + problem.run(); + break; + } + case 6: + { + PDEProblem<2, 6> problem(user_inputs); + problem.run(); + break; + } + default: + throw std::runtime_error("Invalid element degree"); + } + break; + } + case 3: + { + userInputParameters<3> user_inputs(input_file_reader, + input_file_reader.parameter_handler); + switch (user_inputs.spatial_discretization.degree) + { + case 1: + { + PDEProblem<3, 1> problem(user_inputs); + problem.run(); + break; + } + case 2: + { + PDEProblem<3, 2> problem(user_inputs); + problem.run(); + break; + } + case 3: + { + PDEProblem<3, 3> problem(user_inputs); + problem.run(); + break; + } + case 4: + { + PDEProblem<3, 4> problem(user_inputs); + problem.run(); + break; + } + case 5: + { + PDEProblem<3, 5> problem(user_inputs); + problem.run(); + break; + } + case 6: + { + PDEProblem<3, 6> problem(user_inputs); + problem.run(); + break; + } + default: + throw std::runtime_error("Invalid element degree"); + } + break; + } + default: + throw std::runtime_error("Invalid number of dimensions"); + } + } + + catch (std::exception &exc) + { + std::cerr << '\n' + << '\n' + << "----------------------------------------------------" << '\n'; + std::cerr << "Exception on processing: " << '\n' + << exc.what() << '\n' + << "Aborting!" << '\n' + << "----------------------------------------------------" << '\n'; + return 1; + } + + catch (...) + { + std::cerr << '\n' + << '\n' + << "----------------------------------------------------" << '\n'; + std::cerr << "Unknown exception!" << '\n' + << "Aborting!" << '\n' + << "----------------------------------------------------" << '\n'; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/applications/multigrid_test/parameters.prm b/applications/multigrid_test/parameters.prm new file mode 100644 index 000000000..70de725d1 --- /dev/null +++ b/applications/multigrid_test/parameters.prm @@ -0,0 +1,30 @@ +set Number of dimensions = 2 + +set Domain size X = 1.0 +set Domain size Y = 1.0 +set Domain size Z = 1.0 + +set Subdivisions X = 1 +set Subdivisions Y = 1 +set Subdivisions Z = 1 +set Refine factor = 5 +set Element degree = 1 + +set Boundary condition for variable phi = NON_UNIFORM_DIRICHLET + +subsection Pinning point: phi + set value = 2147483647 + set x = 0.0 + set y = 0.0 + set z = 0.0 +end + +subsection Linear solver parameters: phi + set Tolerance type = ABSOLUTE_RESIDUAL + set Tolerance value = 1.0e-8 + set Maximum linear solver iterations = 100 + set Preconditioner = GMG + set Smoothing range = 15 + set Smoother iterations = 5 + set Eigenvalue CG iterations = 10 +end \ No newline at end of file diff --git a/automatic_tests/main.cc b/automatic_tests/main.cc index f5526507c..c9fd00bdf 100644 --- a/automatic_tests/main.cc +++ b/automatic_tests/main.cc @@ -5,8 +5,8 @@ #include "core/variableAttributes.h" #include "equations.cc" -#include #include +#include #include // Header file for postprocessing that may or may not exist @@ -38,7 +38,7 @@ main(int argc, char **argv) std::string parameters_filename; try { - ParseCommandLineOpts cli_options(argc, argv); + parseCMDOptions cli_options(argc, argv); parameters_filename = cli_options.getParametersFilename(); } catch (const char *msg) diff --git a/include/config.h.in b/include/config.h.in index 622a2f0f4..cb60722b4 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -10,4 +10,70 @@ #cmakedefine PRISMS_PF_WITH_CALIPER +// Macro for template instantations with +#define INSTANTIATE_TRI_TEMPLATE(class_name) \ + template class class_name<1, 1, float>; \ + template class class_name<1, 2, float>; \ + template class class_name<1, 3, float>; \ + template class class_name<1, 4, float>; \ + template class class_name<1, 5, float>; \ + template class class_name<1, 6, float>; \ + template class class_name<1, 1, double>; \ + template class class_name<1, 2, double>; \ + template class class_name<1, 3, double>; \ + template class class_name<1, 4, double>; \ + template class class_name<1, 5, double>; \ + template class class_name<1, 6, double>; \ + template class class_name<2, 1, float>; \ + template class class_name<2, 2, float>; \ + template class class_name<2, 3, float>; \ + template class class_name<2, 4, float>; \ + template class class_name<2, 5, float>; \ + template class class_name<2, 6, float>; \ + template class class_name<2, 1, double>; \ + template class class_name<2, 2, double>; \ + template class class_name<2, 3, double>; \ + template class class_name<2, 4, double>; \ + template class class_name<2, 5, double>; \ + template class class_name<2, 6, double>; \ + template class class_name<3, 1, float>; \ + template class class_name<3, 2, float>; \ + template class class_name<3, 3, float>; \ + template class class_name<3, 4, float>; \ + template class class_name<3, 5, float>; \ + template class class_name<3, 6, float>; \ + template class class_name<3, 1, double>; \ + template class class_name<3, 2, double>; \ + template class class_name<3, 3, double>; \ + template class class_name<3, 4, double>; \ + template class class_name<3, 5, double>; \ + template class class_name<3, 6, double>; + +// Macro for template instantations with +#define INSTANTIATE_BI_TEMPLATE(class_name) \ + template class class_name<1, 1>; \ + template class class_name<1, 2>; \ + template class class_name<1, 3>; \ + template class class_name<1, 4>; \ + template class class_name<1, 5>; \ + template class class_name<1, 6>; \ + template class class_name<2, 1>; \ + template class class_name<2, 2>; \ + template class class_name<2, 3>; \ + template class class_name<2, 4>; \ + template class class_name<2, 5>; \ + template class class_name<2, 6>; \ + template class class_name<3, 1>; \ + template class class_name<3, 2>; \ + template class class_name<3, 3>; \ + template class class_name<3, 4>; \ + template class class_name<3, 5>; \ + template class class_name<3, 6>; + +// Macro for template instantations with +#define INSTANTIATE_UNI_TEMPLATE(class_name) \ + template class class_name<1>; \ + template class class_name<2>; \ + template class class_name<3>; + #endif diff --git a/include/core/ParseCommandLineOpts.h b/include/core/ParseCommandLineOpts.h deleted file mode 100644 index bd17defec..000000000 --- a/include/core/ParseCommandLineOpts.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef INCLUDE_PARSECOMMANDLINE_OPTS_H_ -#define INCLUDE_PARSECOMMANDLINE_OPTS_H_ - -#include - -#include -#include -#include -#include -#include - -class ParseCommandLineOpts -{ -public: - ParseCommandLineOpts(int &_argc, char **argv) - { - argc = _argc; - for (int i = 1; i < argc; ++i) - tokens.push_back(std::string(argv[i])); - } - - std::string - getParametersFilename() - { - std::string parameters_filename; - - if (argc == 3) - { - if (cmdOptionExists("-i")) - { - parameters_filename = getCmdOption("-i"); - if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - std::cout << "Using the input parameter file: " << parameters_filename - << std::endl; - } - } - else - { - throw("Invalid command line option given. The only argument should " - "be to specify the input file name."); - } - } - else if (argc == 2) - { - if (cmdOptionExists("-i")) - { - parameters_filename = "parameters.prm"; - std::ifstream ifs_prm(parameters_filename); - std::ifstream ifs_in("parameters.in"); - if (!ifs_prm && ifs_in) - throw("The previous extension .in for the parameters file is no " - "longer accepted. Please rename parameters.in as " - "parameters.prm"); - if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - std::cout << "Using the input parameter file: " << parameters_filename - << std::endl; - } - } - else - { - throw("Invalid command line option given. The only argument should " - "be to specify the input file name."); - } - } - else if (argc == 1) - { - parameters_filename = "parameters.prm"; - std::ifstream ifs_prm(parameters_filename); - std::ifstream ifs_in("parameters.in"); - if (!ifs_prm && ifs_in) - throw("The previous extension .in for the parameters file is no longer " - "accepted. Please rename parameters.in as parameters.prm"); - if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - std::cout << "Using the input parameter file: " << parameters_filename - << std::endl; - } - } - else - { - throw("Too many command line arguments were given. The only argument " - "should be to specify the input file name."); - } - - return parameters_filename; - } - -private: - int argc; - std::vector tokens; - - const std::string & - getCmdOption(const std::string &option) const - { - std::vector::const_iterator itr; - itr = std::find(tokens.begin(), tokens.end(), option); - if (itr != tokens.end() && ++itr != tokens.end()) - { - return *itr; - } - static const std::string empty_string(""); - return empty_string; - } - - bool - cmdOptionExists(const std::string &option) const - { - return std::find(tokens.begin(), tokens.end(), option) != tokens.end(); - } -}; - -#endif diff --git a/include/core/boundary_conditions/constraint_handler.h b/include/core/boundary_conditions/constraint_handler.h new file mode 100644 index 000000000..93a1744d9 --- /dev/null +++ b/include/core/boundary_conditions/constraint_handler.h @@ -0,0 +1,209 @@ +#ifndef constraint_handler_h +#define constraint_handler_h + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/** + * \brief The class handles the generation and application of boundary conditions based on + * the user-inputs. + */ +template +class constraintHandler +{ +public: + /** + * \brief Constructor. + */ + constraintHandler(const boundaryParameters &_boundary_parameters, + const uint &_global_variable_index); + + /** + * \brief Getter function for constraints (constant reference). + */ + [[nodiscard]] dealii::AffineConstraints & + get_constraints(); + + /** + * \brief Make constraints based on the inputs of the construct. + */ + void + make_constraints(const dealii::MappingQ1 &mapping, + const dealii::DoFHandler &dof_handler); + +private: + /** + * \brief Set the uniform dirichlet constraint. + */ + void + set_dirichlet_constraints(); + + /** + * \brief Set the dirichlet constraint for the pinned point. + */ + void + set_pinned_point(const std::pair> &value_point_pair, + const dealii::DoFHandler &dof_handler); + + std::unique_ptr> constraints; + + boundaryParameters boundary_parameters; + + uint global_variable_index; +}; + +template +constraintHandler::constraintHandler( + const boundaryParameters &_boundary_parameters, + const uint &_global_variable_index) + : constraints(std::make_unique>()) + , boundary_parameters(_boundary_parameters) + , global_variable_index(_global_variable_index) +{ + constraints->clear(); +} + +template +dealii::AffineConstraints & +constraintHandler::get_constraints() +{ + return *constraints; +} + +template +void +constraintHandler::make_constraints( + const dealii::MappingQ1 &mapping, + const dealii::DoFHandler &dof_handler) +{ + // Reinitialize constraints + constraints->reinit(dof_handler.locally_owned_dofs(), + dealii::DoFTools::extract_locally_relevant_dofs(dof_handler)); + + // Make hanging node constraints + dealii::DoFTools::make_hanging_node_constraints(dof_handler, *constraints); + + // First check the normal boundary conditions + const auto &boundary_condition = + boundary_parameters.boundary_condition_list.at(global_variable_index); + + for (const auto &[component, condition] : boundary_condition) + { + for (const auto &[boundary_id, boundary_type] : condition.boundary_condition_map) + { + if (boundary_type == boundaryType::NATURAL) + { + // Do nothing because they are naturally enforced. + continue; + } + else if (boundary_type == boundaryType::DIRICHLET) + { + dealii::VectorTools::interpolate_boundary_values( + mapping, + dof_handler, + boundary_id, + dealii::Functions::ConstantFunction(condition.dirichlet_value_map.at( + boundary_id), + 1), + *constraints); + } + else if (boundary_type == boundaryType::PERIODIC) + { + // Skip boundary ids that are odd since those map to the even faces + if (boundary_id % 2 != 0) + { + continue; + } + // Create a vector of matched pairs that we fill and enforce upon the + // constaints + std::vector::cell_iterator>> + periodicity_vector; + + // Determine the direction + const uint direction = std::floor(boundary_id / dim); + + // Collect the matched pairs on the coarsest level of the mesh + dealii::GridTools::collect_periodic_faces(dof_handler, + boundary_id, + boundary_id + 1, + direction, + periodicity_vector); + + // Set constraints + dealii::DoFTools::make_periodicity_constraints(periodicity_vector, + *constraints); + } + else if (boundary_type == boundaryType::NEUMANN) + { + Assert(false, FeatureNotImplemented("Neumann boundary conditions")); + } + else if (boundary_type == boundaryType::NON_UNIFORM_DIRICHLET) + { + dealii::VectorTools::interpolate_boundary_values( + mapping, + dof_handler, + boundary_id, + nonuniformDirichlet(global_variable_index, boundary_id), + *constraints); + } + else if (boundary_type == boundaryType::NON_UNIFORM_NEUMANN) + { + Assert(false, + FeatureNotImplemented("Nonuniform neumann boundary conditions")); + } + } + } + + // Second check for pinned points, if they exist + if (boundary_parameters.pinned_point_list.find(global_variable_index) != + boundary_parameters.pinned_point_list.end()) + { + set_pinned_point(boundary_parameters.pinned_point_list.at(global_variable_index), + dof_handler); + } + + // Close constraints + constraints->close(); +} + +template +void +constraintHandler::set_pinned_point( + const std::pair> &value_point_pair, + const dealii::DoFHandler &dof_handler) +{ + Assert(field_type == fieldType::VECTOR, + FeatureNotImplemented("Pinned points for vector fields")); + + for (const auto &cell : dof_handler.active_cell_iterators()) + { + if (cell->is_locally_owned()) + { + for (unsigned int i = 0; i < dealii::GeometryInfo::vertices_per_cell; ++i) + { + // Check if the vertex is the target vertex + if (value_point_pair.second.distance(cell->vertex(i)) < + 1.0e-2 * cell->diameter()) + { + unsigned int nodeID = cell->vertex_dof_index(i, 0); + constraints->add_line(nodeID); + constraints->set_inhomogeneity(nodeID, value_point_pair.first); + } + } + } + } +} + +#endif \ No newline at end of file diff --git a/include/core/boundary_conditions/nonUniformDirichletBC.h b/include/core/boundary_conditions/nonUniformDirichletBC.h deleted file mode 100644 index c3615664d..000000000 --- a/include/core/boundary_conditions/nonUniformDirichletBC.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef INCLUDE_NONUNIFORMDIRICHLETBCS_H_ -#define INCLUDE_NONUNIFORMDIRICHLETBCS_H_ - -#include - -template -class NonUniformDirichletBC : public dealii::Function -{ -public: - dealii::Vector values; - - NonUniformDirichletBC(const unsigned int _index, - const unsigned int _direction, - const double _time, - MatrixFreePDE *_matrix_free_pde) - : dealii::Function(1) - , index(_index) - , direction(_direction) - , time(_time) - , matrix_free_pde(_matrix_free_pde) - { - std::srand(dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) + 1); - } - - // IC for scalar values - [[nodiscard]] double - value(const dealii::Point &p, - [[maybe_unused]] const unsigned int component = 0) const override - { - double scalar_BC = 0.0; - dealii::Vector vector_BC(dim); - - matrix_free_pde - ->setNonUniformDirichletBCs(p, index, direction, time, scalar_BC, vector_BC); - - return scalar_BC; - }; - -private: - const unsigned int index; - const unsigned int direction; - const double time; - MatrixFreePDE *matrix_free_pde; -}; - -template -class NonUniformDirichletBCVector : public dealii::Function -{ -public: - dealii::Vector values; - - NonUniformDirichletBCVector(const unsigned int _index, - const unsigned int _direction, - const double _time, - MatrixFreePDE *_matrix_free_pde) - : dealii::Function(dim) - , index(_index) - , direction(_direction) - , time(_time) - , matrix_free_pde(_matrix_free_pde) - { - std::srand(dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) + 1); - } - - // IC for vector values - void - vector_value(const dealii::Point &p, - dealii::Vector &vector_BC) const override - { - double scalar_BC = 0.0; - - vector_BC.reinit(dim); - matrix_free_pde - ->setNonUniformDirichletBCs(p, index, direction, time, scalar_BC, vector_BC); - }; - -private: - const unsigned int index; - const unsigned int direction; - const double time; - MatrixFreePDE *matrix_free_pde; -}; - -#endif // INCLUDE_NONUNIFORMDIRICHLETBCS_H_ diff --git a/include/core/boundary_conditions/nonuniform_dirichlet.h b/include/core/boundary_conditions/nonuniform_dirichlet.h new file mode 100644 index 000000000..eeb0d44be --- /dev/null +++ b/include/core/boundary_conditions/nonuniform_dirichlet.h @@ -0,0 +1,115 @@ +#ifndef nonuniform_dirichlet_h +#define nonuniform_dirichlet_h + +#include +#include + +#include + +/** + * \brief Forward declaration of user-facing implementation + */ +template +class customNonuniformDirichlet; + +/** + * \brief Function for user-implemented nonuniform dirichlet boundary condition. + */ +template +class nonuniformDirichlet : public dealii::Function +{ +public: + /** + * \brief Constructor. + */ + nonuniformDirichlet(const uint &_index, const uint &_boundary_id) + : dealii::Function((field_type == fieldType::VECTOR) ? dim : 1) + , index(_index) + , boundary_id(_boundary_id) {}; + + /** + * \brief Scalar value. + */ + double + value(const dealii::Point &p, const unsigned int component = 0) const override; + + /** + * \brief Vector value. + */ + void + vector_value(const dealii::Point &p, dealii::Vector &value) const override; + +private: + const uint index; + + const uint boundary_id; + + customNonuniformDirichlet custom_nonuniform_dirichlet; +}; + +template +inline double +nonuniformDirichlet::value( + const dealii::Point &p, + [[maybe_unused]] const unsigned int component) const +{ + // Initialize passed variables to zero + double scalar_value = 0.0; + dealii::Vector vector_value(dim); + + // Pass variables to user-facing function to evaluate + custom_nonuniform_dirichlet + .set_nonuniform_dirichlet(index, boundary_id, 0, p, scalar_value, vector_value(0)); + + return scalar_value; +} + +template +inline void +nonuniformDirichlet::vector_value(const dealii::Point &p, + dealii::Vector &value) const +{ + // Initialize passed variables to zero + double scalar_value = 0.0; + dealii::Vector vector_value(dim); + + // Pass variables to user-facing function to evaluate + for (uint i = 0; i < dim; i++) + { + custom_nonuniform_dirichlet.set_nonuniform_dirichlet(index, + boundary_id, + i, + p, + scalar_value, + vector_value(i)); + } + + value = vector_value; +} + +/** + * \brief User-facing implementation of nonuniform boundary conditions + */ +template +class customNonuniformDirichlet +{ +public: + /** + * \brief Constructor. + */ + customNonuniformDirichlet() = default; + + /** + * \brief Function that passes the value/vector and point that are set in the nonuniform + * dirichlet. + */ + void + set_nonuniform_dirichlet(const uint &index, + const uint &boundary_id, + const uint &component, + const dealii::Point &point, + double &scalar_value, + double &vector_component_value) const; +}; + +#endif \ No newline at end of file diff --git a/include/core/boundary_conditions/varBCs.h b/include/core/boundary_conditions/varBCs.h deleted file mode 100644 index d1f0beb1a..000000000 --- a/include/core/boundary_conditions/varBCs.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * varBCs.h - * - * Created on: Feb 27, 2017 - * Author: stephendewitt - */ - -#ifndef INCLUDE_VARBCS_H_ -#define INCLUDE_VARBCS_H_ - -#include - -// BC type enum -enum BC_type -{ - NATURAL, - DIRICHLET, - PERIODIC, - NON_UNIFORM_DIRICHLET, - NEUMANN -}; - -// BC object declaration -template -class varBCs -{ -public: - std::vector var_BC_type; - std::vector var_BC_val; -}; - -#endif /* INCLUDE_VARBCS_H_ */ diff --git a/include/core/boundary_conditions/vectorBCFunction.h b/include/core/boundary_conditions/vectorBCFunction.h deleted file mode 100644 index 63b3a3aa5..000000000 --- a/include/core/boundary_conditions/vectorBCFunction.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * vectorBCFunction.h - * - * Created on: Feb 22, 2017 - * Author: stephendewitt - */ - -#ifndef INCLUDE_VECTORBCFUNCTION_H_ -#define INCLUDE_VECTORBCFUNCTION_H_ - -#include -#include -#include - -template -class vectorBCFunction : public dealii::Function -{ -public: - vectorBCFunction(const std::vector &BC_values); - virtual void - vector_value(const dealii::Point &p, - dealii::Vector &values) const override; - - virtual void - vector_value_list(const std::vector> &points, - std::vector> &value_list) const override; - -private: - const std::vector BC_values; -}; - -#endif \ No newline at end of file diff --git a/include/core/conditional_ostreams.h b/include/core/conditional_ostreams.h new file mode 100644 index 000000000..5f0944160 --- /dev/null +++ b/include/core/conditional_ostreams.h @@ -0,0 +1,52 @@ +#ifndef conditional_ostreams_h +#define conditional_ostreams_h + +#include +#include + +#include + +/** + * \brief A class that allows printing to different output streams that are classified + * based on their verbosity. For now, this consists of two stream the release and debug. + * The debug stream provides more information that may be useful when debugging. + */ +class conditionalOStreams +{ +public: + /** + * \brief Constructor. + */ + conditionalOStreams(); + + /** + * \brief Destructor. + */ + ~conditionalOStreams(); + + /** + * \brief Generic parallel output stream. Used for essential information in release and + * debug mode. + */ + static const dealii::ConditionalOStream pout_base; + + /** + * \brief Verbose parallel output stream. Used for additional information in debug mode. + */ + static const dealii::ConditionalOStream pout_verbose; + + /** + * \brief Log output stream for writing a summary.log file. + */ + static dealii::ConditionalOStream & + pout_summary(); + + // summary.log file + static std::ofstream summary_log_file; + +private: + // summary.log output stream + static dealii::ConditionalOStream pout_summary_instance; +}; + +#endif \ No newline at end of file diff --git a/include/core/exceptions.h b/include/core/exceptions.h index 73376bdd9..b58343e21 100644 --- a/include/core/exceptions.h +++ b/include/core/exceptions.h @@ -1,5 +1,5 @@ -#ifndef EXCEPTIONS_H -#define EXCEPTIONS_H +#ifndef exceptions_h +#define exceptions_h #include diff --git a/include/core/fields.h b/include/core/fields.h deleted file mode 100644 index 9a2cf8a0e..000000000 --- a/include/core/fields.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef FIELDS_H -#define FIELDS_H - -#include - -#include -#include -#include -#include - -/** - * \brief Field class that handles the attributes of each field. - */ -template -class Field -{ -public: - /** - * \brief Constructor. - */ - Field(fieldType _type, PDEType _pdetype, std::string _name); - - fieldType type; - PDEType pdetype; - std::string name; - unsigned int index; - unsigned int startIndex; - unsigned int numComponents; - bool hasDirichletBCs; - bool hasnonuniformDirichletBCs; - bool hasNeumannBCs; - -private: - /** - * \brief Total global fields. - */ - static unsigned int fieldCount; - - /** - * \brief Total global indices - */ - static unsigned int indexCount; - - /** - * \brief Validate the PDEtype and fieldType passed into the constructor. - */ - void - validate_enum_types(); - - /** - * \brief Initialize the number of components based on whether the field is a scalar or - * vector. - */ - void - init_components(); - - /** - * \brief Initialize the boundary condition conditionals to false. - */ - void - init_BCs(); -}; - -// initialize static variables -template -unsigned int Field::fieldCount = 0; - -template -unsigned int Field::indexCount = 0; - -// constructor -template -Field::Field(fieldType _type, PDEType _pdetype, std::string _name) - : type(_type) - , pdetype(_pdetype) - , name(std::move(_name)) -{ - validate_enum_types(); - - // Set the index to the current field count and increment field count as new field is - // being created - index = fieldCount++; - startIndex = indexCount; - - init_components(); - - init_BCs(); -} - -template -void -Field::validate_enum_types() -{ - // Validate fieldType - switch (type) - { - case fieldType::SCALAR: - case fieldType::VECTOR: - break; - default: - throw std::invalid_argument("Unknown field type in Field constructor."); - } - - switch (pdetype) - { - case PDEType::EXPLICIT_TIME_DEPENDENT: - case PDEType::AUXILIARY: - case PDEType::IMPLICIT_TIME_DEPENDENT: - case PDEType::TIME_INDEPENDENT: - break; - default: - throw std::invalid_argument("Unknown PDE type in Field constructor."); - } -} - -template -void -Field::init_components() -{ - switch (type) - { - case SCALAR: - indexCount += 1; - numComponents = 1; - break; - case VECTOR: - indexCount += dim; - numComponents = dim; - break; - default: - break; - } -} - -template -void -Field::init_BCs() -{ - // Default assignment of BCs - hasDirichletBCs = false; - hasnonuniformDirichletBCs = false; - hasNeumannBCs = false; -} - -#endif diff --git a/include/core/initial_conditions/initialConditions.h b/include/core/initial_conditions/initialConditions.h deleted file mode 100644 index 17125e188..000000000 --- a/include/core/initial_conditions/initialConditions.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef INCLUDE_INITIALCONDITIONS_H_ -#define INCLUDE_INITIALCONDITIONS_H_ - -#include -#include - -template -class InitialCondition : public dealii::Function -{ -public: - const unsigned int index; - const userInputParameters userInputs; - dealii::Vector values; - - InitialCondition(const unsigned int _index, - const userInputParameters _userInputs, - MatrixFreePDE *_matrix_free_pde) - : dealii::Function(1) - , index(_index) - , userInputs(_userInputs) - , matrix_free_pde(_matrix_free_pde) - { - std::srand(dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) + 1); - } - - // IC for scalar values - [[nodiscard]] double - value(const dealii::Point &p, - [[maybe_unused]] const unsigned int component = 0) const override - { - double scalar_IC = 0.0; - dealii::Vector vector_IC(dim); - - matrix_free_pde->setInitialCondition(p, index, scalar_IC, vector_IC); - return scalar_IC; - }; - -private: - MatrixFreePDE *matrix_free_pde; -}; - -template -class InitialConditionVector : public dealii::Function -{ -public: - const unsigned int index; - const userInputParameters userInputs; - dealii::Vector values; - - InitialConditionVector(const unsigned int _index, - const userInputParameters _userInputs, - MatrixFreePDE *_matrix_free_pde) - : dealii::Function(dim) - , index(_index) - , userInputs(_userInputs) - , matrix_free_pde(_matrix_free_pde) - { - std::srand(dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) + 1); - } - - // IC for vector values - void - vector_value(const dealii::Point &p, - dealii::Vector &vector_IC) const override - { - double scalar_IC = 0.0; - vector_IC.reinit(dim); - matrix_free_pde->setInitialCondition(p, index, scalar_IC, vector_IC); - }; - -private: - MatrixFreePDE *matrix_free_pde; -}; - -#endif diff --git a/include/core/postprocessing/placeholder.h b/include/core/initial_conditions/initial_conditions.h similarity index 100% rename from include/core/postprocessing/placeholder.h rename to include/core/initial_conditions/initial_conditions.h diff --git a/include/core/invm_handler.h b/include/core/invm_handler.h new file mode 100644 index 000000000..5143b5928 --- /dev/null +++ b/include/core/invm_handler.h @@ -0,0 +1,142 @@ +#ifndef invm_handler_h +#define invm_handler_h + +#include + +#include +#include +#include + +/** + * \brief This class handles the computation and access of the inverted mass matrix for + * explicit solves. + */ +template +class invmHandler +{ +public: + /** + * \brief Constructor. + */ + invmHandler(const AttributesList &_variable_attributes); + + /** + * \brief Destructor. + */ + ~invmHandler(); + + /** + * \brief Compute the mass matrix for scalar/vector fields. + */ + void + compute_invm(); + + /** + * \brief Recompute the mass matrix for scalar/vector fields. This just points to + * compute_invm() and is used for style. + */ + void + recompute_invm(); + + /** + * \brief Getter function for the mass matrix for the given field index (constant + * reference). + */ + const dealii::LinearAlgebra::distributed::Vector & + get_invm(const uint &index); + +private: + /** + * \brief Variable attributes. This is used to determine the proper return type for the + * invm when given a field index. + */ + const AttributesList &variable_attributes; + + /** + * \brief Inverse of the mass matrix for scalar fields. + */ + dealii::LinearAlgebra::distributed::Vector invm_scalar; + + /** + * \brief Inverse of the mass matrix for vector fields. + */ + dealii::LinearAlgebra::distributed::Vector invm_vector; + + /** + * \brief Whether a scalar invm is needed. + */ + bool scalar_needed = false; + + /** + * \brief Whether a vector invm is needed. + */ + bool vector_needed = false; +}; + +template +invmHandler::invmHandler(const AttributesList &_variable_attributes) + : variable_attributes(_variable_attributes) +{ + for (const auto &[index, variable] : variable_attributes) + { + if (variable.field_type == fieldType::SCALAR) + { + scalar_needed = true; + } + if (variable.field_type == fieldType::VECTOR) + { + vector_needed = true; + } + } +} + +template +inline void +invmHandler::compute_invm() +{} + +template +inline void +invmHandler::recompute_invm() +{ + this->compute_invm(); +} + +template +inline const dealii::LinearAlgebra::distributed::Vector & +invmHandler::get_invm(const uint &index) +{ + Assert(variable_attributes.at(index) != variable_attributes.end(), + dealii::ExcMessage( + "Invalid index. The provided index does not have an entry in the variable " + "attributes that were provided to the constructor.")); + + if (variable_attributes.at(index).field_type == fieldType::SCALAR) + { + Assert(scalar_needed, + dealii::ExcMessage( + "The invm for scalar fields is marked as not needed. Make sure the " + "variable attributes correspond with the provided index.")); + Assert(invm_scalar.size() == 0, + dealii::ExcMessage("The scalar invm has size 0. Please make sure to call " + "compute_invm() prior to calling the getter function.")); + + return invm_scalar; + } + else if (variable_attributes.at(index).field_type == fieldType::VECTOR) + { + Assert(vector_needed, + dealii::ExcMessage( + "The invm for vector fields is marked as not needed. Make sure the " + "variable attributes correspond with the provided index.")); + Assert(invm_vector.size() == 0, + dealii::ExcMessage("The vector invm has size 0. Please make sure to call " + "compute_invm() prior to calling the getter function.")); + + return invm_vector; + } + + Assert(false, dealii::ExcMessage("Invalid field type.")); +} + +#endif \ No newline at end of file diff --git a/include/core/matrixFreePDE.h b/include/core/matrixFreePDE.h deleted file mode 100644 index bdc070cb8..000000000 --- a/include/core/matrixFreePDE.h +++ /dev/null @@ -1,537 +0,0 @@ -#ifndef MATRIXFREEPDE_H -#define MATRIXFREEPDE_H - -// dealii headers -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// PRISMS headers -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace dealii; - -/** - * \brief This is the abstract base class for the matrix free implementation of parabolic - * and elliptic BVP's, and supports MPI, threads and vectorization (Hybrid Parallel). This - * class contains the parallel data structures, mesh (referred to as triangulation), - * parallel degrees of freedom distribution, constraints, and general utility methods. - * - * All the physical models in this package inherit this base class. - * - * \tparam dim The number of dimensions in the problem. - * \tparam degree The polynomial degree of the shape functions. - */ -template -class MatrixFreePDE : public Subscriptor -{ -public: - /** - * \brief Class contructor. - */ - MatrixFreePDE(userInputParameters); - - /** - * \brief Class destructor. - */ - ~MatrixFreePDE() override; - - /** - * \brief Initializes the mesh, degrees of freedom, constraints and data structures - * using the user provided inputs in the application parameters file. - */ - virtual void - init(); - - /** - * \brief Create the mesh with the user provided domain sizes. - * - * \param tria Triangulation object. It must be empty prior to calling this function. - */ - virtual void - create_triangulation(parallel::distributed::Triangulation &tria) const; - - /** - * \brief Initializes the data structures for enabling unit tests. - * - * \details This method initializes the MatrixFreePDE object with a fixed geometry, - * discretization and other custom selected options specifically to help with unit - * tests, and should not be called in any of the physical models. - * - * \param _fields Vector of PDE descriptions (e.g., scalar/vector) for each field. - */ - void - initForTests(std::vector> _fields); - - /** - * \brief This method implements the time stepping algorithm and invokes the - * solveIncrement() method. - */ - void - solve(); - - /** - * \brief This method essentially converts the MatrixFreePDE object into a matrix object - * which can be used with matrix free iterative solvers. Provides the A*x functionality - * for solving the system of equations Ax=b. - * - * \param dst The destination vector. - * \param src The source vector. - */ - void - vmult(dealii::LinearAlgebra::distributed::Vector &dst, - const dealii::LinearAlgebra::distributed::Vector &src) const; - - /** - * \brief Vector of all the physical fields in the problem. Fields are identified by - * dimentionality (SCALAR/VECTOR), the kind of PDE (ELLIPTIC/PARABOLIC) used to compute - * them and a character identifier (e.g.: "c" for composition) which is used to write - * the fields to the output files. - */ - std::vector> fields; - - /** - * \brief Create the vector of all physical fields in the problem. - */ - void - buildFields(); - - /** - * \brief Parallel message stream. - */ - ConditionalOStream pcout; - - /** - * \brief Set the initial condition for all fields. This function is overriden in each - * application. - * - * \param p The point at which the initial condition is evaluated. - * \param index The index of the field being evaluated. - * \param scalar_IC Return variable for scalar fields. - * \param vector_IC Return variable for vector fields. - */ - virtual void - setInitialCondition([[maybe_unused]] const Point &p, - [[maybe_unused]] const unsigned int index, - [[maybe_unused]] double &scalar_IC, - [[maybe_unused]] Vector &vector_IC) = 0; - - /** - * \brief Set the spatially or temporally non-uniform boundary conditions. This function - * is overriden in each application. - * - * \param p The point at which the boundary condition is evaluated. - * \param index The index of the field being evaluated. - * \param direction The face of the boundary condition. - * \param time The time at which the boundary condition is evaluated. - * \param scalar_BC Return variable for scalar fields. - * \param vector_BC Return variable for vector fields. - */ - virtual void - setNonUniformDirichletBCs([[maybe_unused]] const Point &p, - [[maybe_unused]] const unsigned int index, - [[maybe_unused]] const unsigned int direction, - [[maybe_unused]] const double time, - [[maybe_unused]] double &scalar_BC, - [[maybe_unused]] Vector &vector_BC) = 0; - -protected: - userInputParameters userInputs; - - unsigned int totalDOFs; - - // The attributes of the primary field variables and - // the postprocessing field variables - const AttributesList &var_attributes; - const AttributesList &pp_attributes; - - // Elasticity matrix variables - const static unsigned int CIJ_tensor_size = 2 * dim - 1 + dim / 3; - - // Method to reinitialize the mesh, degrees of freedom, constraints and data - // structures when the mesh is adapted - void - reinit(); - - /** - * Method to reassign grains when multiple grains are stored in a single order - * parameter. - */ - void - reassignGrains(); - - std::vector> simplified_grain_representations; - - /** - * Method to solve each time increment of a time-dependent problem. For - * time-independent problems this method is called only once. This method - * solves for all the fields in a staggered manner (one after another) and - * also invokes the corresponding solvers: Explicit solver for Parabolic - * problems, Implicit (matrix-free) solver for Elliptic problems. - */ - virtual void - solveIncrement(bool skip_time_dependent); - /* Method to write solution fields to vtu and pvtu (parallel) files. - * - * This method can be enabled/disabled by setting the flag writeOutput to - * true/false. Also, the user can select how often the solution files are - * written by setting the flag skipOutputSteps in the parameters file. - */ - void - outputResults(); - - /*Parallel mesh object which holds information about the FE nodes, elements - * and parallel domain decomposition - */ - parallel::distributed::Triangulation triangulation; - /*A vector of finite element objects used in a model. For problems with only - *one primal field, the size of this vector is one,otherwise the size is the - *number of primal fields in the problem. - */ - std::vector *> FESet; - /*A vector of all the constraint sets in the problem. A constraint set is a - *map which holds the mapping between the degrees of freedom and the - *corresponding degree of freedom constraints. Currently the type of - *constraints stored are either Dirichlet boundary conditions or hanging node - *constraints for adaptive meshes. - */ - std::vector *> constraintsDirichletSet, - constraintsOtherSet; - /*A vector of all the degree of freedom objects is the problem. A degree of - *freedom object handles the serial/parallel distribution of the degrees of - *freedom for all the primal fields in the problem.*/ - std::vector *> dofHandlersSet; - - /*A vector of the locally relevant degrees of freedom. Locally relevant degrees of - *freedom in a parallel implementation is a collection of the degrees of freedom owned - *by the current processor and the surrounding ghost nodes which are required for the - *field computations in this processor. - */ - std::vector locally_relevant_dofsSet; - /*Copies of constraintSet elements, but stored as non-const to enable application of - * constraints.*/ - std::vector *> constraintsDirichletSet_nonconst, - constraintsOtherSet_nonconst; - /*Copies of dofHandlerSet elements, but stored as non-const.*/ - std::vector *> dofHandlersSet_nonconst; - /*Copies of locally_relevant_dofsSet elements, but stored as non-const.*/ - std::vector locally_relevant_dofsSet_nonconst; - /*Vector all the solution vectors in the problem. In a multi-field problem, each primal - * field has a solution vector associated with it.*/ - std::vector *> solutionSet; - /*Vector all the residual (RHS) vectors in the problem. In a multi-field problem, each - * primal field has a residual vector associated with it.*/ - std::vector *> residualSet; - /*Vector of parallel solution transfer objects. This is used only when adaptive meshing - * is enabled.*/ - std::vector> *> - soltransSet; - - // matrix free objects - /*Object of class MatrixFree. This is primarily responsible for all the - *base matrix free functionality of this MatrixFreePDE class. Refer to - *deal.ii documentation of MatrixFree class for details. - */ - MatrixFree matrixFreeObject; - /*Vector to store the inverse of the mass matrix diagonal for scalar fields. - * Due to the choice of spectral elements with Guass-Lobatto quadrature, the - * mass matrix is diagonal.*/ - dealii::LinearAlgebra::distributed::Vector invMscalar; - /*Vector to store the inverse of the mass matrix diagonal for vector fields. - * Due to the choice of spectral elements with Guass-Lobatto quadrature, the - * mass matrix is diagonal.*/ - dealii::LinearAlgebra::distributed::Vector invMvector; - /*Vector to store the solution increment. This is a temporary vector used - * during implicit solves of the Elliptic fields.*/ - dealii::LinearAlgebra::distributed::Vector dU_vector, dU_scalar; - - // matrix free methods - /*Current field index*/ - unsigned int currentFieldIndex; - /*Method to compute the inverse of the mass matrix*/ - void - computeInvM(); - - /*Method to compute an explicit timestep*/ - void - updateExplicitSolution(unsigned int fieldIndex); - - /*Method to compute an implicit timestep*/ - bool - updateImplicitSolution(unsigned int fieldIndex, unsigned int nonlinear_iteration_index); - - /*Method to apply boundary conditions*/ - void - applyBCs(unsigned int fieldIndex); - - /** - * \brief Compute element volume for the triangulation - */ - void - compute_element_volume(); - - /** - * \brief Vector that stores element volumes - */ - dealii::AlignedVector> element_volume; - - /*Method to compute the right hand side (RHS) residual vectors*/ - void - computeExplicitRHS(); - void - computeNonexplicitRHS(); - - // virtual methods to be implemented in the derived class - /*Method to calculate LHS(implicit solve)*/ - void - getLHS(const MatrixFree &data, - dealii::LinearAlgebra::distributed::Vector &dst, - const dealii::LinearAlgebra::distributed::Vector &src, - const std::pair &cell_range) const; - - bool generatingInitialGuess; - void - getLaplaceLHS(const MatrixFree &data, - dealii::LinearAlgebra::distributed::Vector &dst, - const dealii::LinearAlgebra::distributed::Vector &src, - const std::pair &cell_range) const; - - void - setNonlinearEqInitialGuess(); - void - computeLaplaceRHS(unsigned int fieldIndex); - void - getLaplaceRHS(const MatrixFree &data, - dealii::LinearAlgebra::distributed::Vector &dst, - const dealii::LinearAlgebra::distributed::Vector &src, - const std::pair &cell_range) const; - - /*Method to calculate RHS (implicit/explicit). This is an abstract method, so - * every model which inherits MatrixFreePDE has to implement this - * method.*/ - void - getExplicitRHS( - const MatrixFree &data, - std::vector *> &dst, - const std::vector *> &src, - const std::pair &cell_range) const; - - void - getNonexplicitRHS( - const MatrixFree &data, - std::vector *> &dst, - const std::vector *> &src, - const std::pair &cell_range) const; - - virtual void - explicitEquationRHS( - [[maybe_unused]] variableContainer> - &variable_list, - [[maybe_unused]] const Point> q_point_loc, - [[maybe_unused]] const VectorizedArray element_volume) const = 0; - - virtual void - nonExplicitEquationRHS( - [[maybe_unused]] variableContainer> - &variable_list, - [[maybe_unused]] const Point> q_point_loc, - [[maybe_unused]] const VectorizedArray element_volume) const = 0; - - virtual void - equationLHS([[maybe_unused]] variableContainer> - &variable_list, - [[maybe_unused]] const Point> q_point_loc, - [[maybe_unused]] const VectorizedArray element_volume) const = 0; - - virtual void - postProcessedFields( - [[maybe_unused]] const variableContainer> - &variable_list, - [[maybe_unused]] variableContainer> - &pp_variable_list, - [[maybe_unused]] const Point> q_point_loc, - [[maybe_unused]] const VectorizedArray element_volume) const {}; - void - computePostProcessedFields( - std::vector *> &postProcessedSet); - - void - getPostProcessedFields( - const MatrixFree &data, - std::vector *> &dst, - const std::vector *> &src, - const std::pair &cell_range); - - // methods to apply dirichlet BC's - /*Map of degrees of freedom to the corresponding Dirichlet boundary - * conditions, if any.*/ - std::vector *> valuesDirichletSet; - /*Virtual method to mark the boundaries for applying Dirichlet boundary - * conditions. This is usually expected to be provided by the user.*/ - void - markBoundaries(parallel::distributed::Triangulation &) const; - /** Method for applying Dirichlet boundary conditions.*/ - void - applyDirichletBCs(); - - /** Method for applying Neumann boundary conditions.*/ - void - applyNeumannBCs(); - - // Methods to apply periodic BCs - void - setPeriodicity(); - void - setPeriodicityConstraints(AffineConstraints *, const DoFHandler *) const; - - /** - * \brief Set constraints to pin the solution to 0 at a certain vertex. This is - * automatically done at the origin if no value terms are detected in your dependencies - * in a time_independent or implicit solve. - * - * \param constraints The constraint set. - * \param dof_handler The list of the degrees of freedom. - * \param target_point The point where the solution is constrained. This is the origin - * by default. - */ - void - set_rigid_body_mode_constraints(AffineConstraints *constraints, - const DoFHandler *dof_handler, - const Point target_point = Point()) const; - - // methods to apply initial conditions - /*Virtual method to apply initial conditions. This is usually expected to be - * provided by the user in IBVP (Initial Boundary Value Problems).*/ - - void - applyInitialConditions(); - - // -------------------------------------------------------------------------- - // Methods for saving and loading checkpoints - // -------------------------------------------------------------------------- - - void - save_checkpoint(); - - void - load_checkpoint_triangulation(); - void - load_checkpoint_fields(); - void - load_checkpoint_time_info(); - - void - move_file(const std::string &, const std::string &); - - void - verify_checkpoint_file_exists(const std::string &filename); - - // -------------------------------------------------------------------------- - // Nucleation methods and variables - // -------------------------------------------------------------------------- - // Vector of all the nuclei seeded in the problem - std::vector> nuclei; - - // Method to get a list of new nuclei to be seeded - void - updateNucleiList(); - std::vector> - getNewNuclei(); - void - getLocalNucleiList(std::vector> &newnuclei) const; - void - safetyCheckNewNuclei(std::vector> newnuclei, - std::vector &conflict_ids); - void - refineMeshNearNuclei(std::vector> newnuclei); - double - weightedDistanceFromNucleusCenter(const Point center, - const std::vector &semiaxes, - const Point q_point_loc, - const unsigned int var_index) const; - VectorizedArray - weightedDistanceFromNucleusCenter(const Point center, - const std::vector &semiaxes, - const Point> q_point_loc, - const unsigned int var_index) const; - - // Method to obtain the nucleation probability for an element, nontrival case - // must be implemented in the subsclass - [[nodiscard]] virtual double - getNucleationProbability(variableValueContainer, - double, - Point, - [[maybe_unused]] unsigned int variable_index) const - { - return 0.0; - }; - - // utility functions - /*Returns index of given field name if exists, else throw error.*/ - unsigned int - getFieldIndex(std::string _name); - - /*Method to compute the integral of a field.*/ - void - computeIntegral( - double &integratedField, - int index, - std::vector *> variableSet); - - // variables for time dependent problems - /*Flag used to see if invM, time stepping in run(), etc are necessary*/ - bool isTimeDependentBVP; - /*Flag used to mark problems with Elliptic fields.*/ - bool isEllipticBVP; - - bool hasExplicitEquation; - bool hasNonExplicitEquation; - // - double currentTime; - unsigned int currentIncrement, currentOutput, currentCheckpoint, - current_grain_reassignment; - - /*Timer and logging object*/ - mutable TimerOutput computing_timer; - - bool first_integrated_var_output_complete; - - // Methods and variables for integration - double integrated_var; - unsigned int integral_index; - std::mutex assembler_lock; - - /*AMR methods*/ - AdaptiveRefinement AMR; -}; - -#endif diff --git a/include/core/matrix_free_operator.h b/include/core/matrix_free_operator.h new file mode 100644 index 000000000..c90cd29c3 --- /dev/null +++ b/include/core/matrix_free_operator.h @@ -0,0 +1,256 @@ +#ifndef matrix_free_operator_h +#define matrix_free_operator_h + +#include +#include +#include + +#include +#include +#include +#include + +/** + * \brief This is the abstract base class for the matrix free implementation of some PDE. + * + * \tparam dim The number of dimensions in the problem. + * \tparam degree The polynomial degree of the shape functions. + * \tparam number Datatype to use for `LinearAlgebra::distributed::Vector`. Either + * double or float. + */ +template +class matrixFreeOperator + : public dealii::MatrixFreeOperators:: + Base> +{ +public: + /** + * \brief Constructor. + */ + matrixFreeOperator(const AttributesList &_attributes_list); + + /** + * \brief Release all memory and return to state like having called the default + * constructor. + */ + void + clear() override; + + /** + * \brief Compute the residual of this operator. This is the b in Ax=b. + */ + void + compute_residual(dealii::LinearAlgebra::distributed::Vector &dst, + const dealii::LinearAlgebra::distributed::Vector &src) const; + + /** + * \brief Compute the diagonal of this operator. + */ + void + compute_diagonal() override; + +protected: + /** + * \brief User-implemented class for the RHS of explicit equations. + */ + virtual void + compute_explicit_RHS( + variableContainer &variable_list, + const dealii::Point> &q_point_loc) const = 0; + + /** + * \brief User-implemented class for the RHS of nonexplicit equations. + */ + virtual void + compute_nonexplicit_RHS( + variableContainer &variable_list, + const dealii::Point> &q_point_loc) const = 0; + + /** + * \brief User-implemented class for the LHS of nonexplicit equations. + */ + virtual void + compute_nonexplicit_LHS( + variableContainer &variable_list, + const dealii::Point> &q_point_loc) const = 0; + +private: + /** + * \brief Apply operator to src and add result in dst. + */ + void + apply_add(dealii::LinearAlgebra::distributed::Vector &dst, + const dealii::LinearAlgebra::distributed::Vector &src) const override; + + /** + * \brief Local application of the operator. + */ + void + local_apply(const dealii::MatrixFree &data, + dealii::LinearAlgebra::distributed::Vector &dst, + const dealii::LinearAlgebra::distributed::Vector &src, + const std::pair &cell_range) const; + + /** + * \brief Local computation of the residual of the operator. + */ + void + compute_local_residual(const dealii::MatrixFree &data, + dealii::LinearAlgebra::distributed::Vector &dst, + const dealii::LinearAlgebra::distributed::Vector &src, + const std::pair &cell_range) const; + + /** + * \brief Local computation of the diagonal of the operator. + */ + void + local_compute_diagonal(const dealii::MatrixFree &data, + dealii::LinearAlgebra::distributed::Vector &dst, + const uint &dummy, + const std::pair &cell_range) const; + + /** + * \brief The attribute list of the relevant variables. + */ + const AttributesList &attributes_list; +}; + +template +matrixFreeOperator::matrixFreeOperator( + const AttributesList &_attributes_list) + : dealii::MatrixFreeOperators:: + Base>() + , attributes_list(_attributes_list) +{} + +template +void +matrixFreeOperator::clear() +{ + dealii::MatrixFreeOperators:: + Base>::clear(); +} + +template +void +matrixFreeOperator::local_apply( + const dealii::MatrixFree &data, + dealii::LinearAlgebra::distributed::Vector &dst, + const dealii::LinearAlgebra::distributed::Vector &src, + const std::pair &cell_range) const +{ + // Constructor for FEEvaluation objects + variableContainer variable_list(data, + attributes_list, + solveType::NONEXPLICIT_LHS); + + // Initialize, evaluate, and submit based on user function. + variable_list.eval_local_operator( + [this](variableContainer &var_list, + const dealii::Point> &q_point_loc) + { + this->compute_nonexplicit_LHS(var_list, q_point_loc); + }, + dst, + src, + cell_range); +} + +template +void +matrixFreeOperator::apply_add( + dealii::LinearAlgebra::distributed::Vector &dst, + const dealii::LinearAlgebra::distributed::Vector &src) const +{ + this->data->cell_loop(&matrixFreeOperator::local_apply, this, dst, src); +} + +template +void +matrixFreeOperator::compute_residual( + dealii::LinearAlgebra::distributed::Vector &dst, + const dealii::LinearAlgebra::distributed::Vector &src) const +{ + this->data->cell_loop(&matrixFreeOperator::compute_local_residual, this, dst, src); +} + +template +void +matrixFreeOperator::compute_local_residual( + const dealii::MatrixFree &data, + dealii::LinearAlgebra::distributed::Vector &dst, + const dealii::LinearAlgebra::distributed::Vector &src, + const std::pair &cell_range) const +{ + // Constructor for FEEvaluation objects + variableContainer variable_list(data, + attributes_list, + solveType::NONEXPLICIT_RHS); + + // Initialize, evaluate, and submit based on user function. + variable_list.eval_local_operator( + [this](variableContainer &var_list, + const dealii::Point> &q_point_loc) + { + this->compute_nonexplicit_RHS(var_list, q_point_loc); + }, + dst, + src, + cell_range); +} + +template +void +matrixFreeOperator::compute_diagonal() +{ + this->inverse_diagonal_entries.reset( + new DiagonalMatrix>()); + dealii::LinearAlgebra::distributed::Vector &inverse_diagonal = + this->inverse_diagonal_entries->get_vector(); + this->data->initialize_dof_vector(inverse_diagonal); + uint dummy = 0; + this->data->cell_loop(&matrixFreeOperator::local_compute_diagonal, + this, + inverse_diagonal, + dummy); + + this->set_constrained_entries_to_one(inverse_diagonal); + + for (uint i = 0; i < inverse_diagonal.locally_owned_size(); ++i) + { + Assert(inverse_diagonal.local_element(i) > 0.0, + dealii::ExcMessage( + "No diagonal entry in a positive definite operator should be zero")); + inverse_diagonal.local_element(i) = 1.0 / inverse_diagonal.local_element(i); + } +} + +template +void +matrixFreeOperator::local_compute_diagonal( + const dealii::MatrixFree &data, + dealii::LinearAlgebra::distributed::Vector &dst, + [[maybe_unused]] const uint &dummy, + const std::pair &cell_range) const +{ + // Field index + uint field_index = 0; + + // Constructor for FEEvaluation objects + variableContainer variable_list(data, + attributes_list, + solveType::NONEXPLICIT_LHS); + + // Initialize, evaluate, and submit diagonal based on user function. + variable_list.eval_local_diagonal( + [this](variableContainer &var_list, + const dealii::Point> &q_point_loc) + { + this->compute_nonexplicit_LHS(var_list, q_point_loc); + }, + dst, + cell_range, + field_index); +} + +#endif \ No newline at end of file diff --git a/include/core/model_variables.h b/include/core/model_variables.h deleted file mode 100644 index 24aed7553..000000000 --- a/include/core/model_variables.h +++ /dev/null @@ -1,22 +0,0 @@ -// Model Variables Class - -#ifndef INCLUDE_MODELVARIABLE_H_ -#define INCLUDE_MODELVARIABLE_H_ - -#include -#include -#include - -struct variable_info -{ - unsigned int global_var_index = 0; - bool is_scalar = true; - bool var_needed = false; - - dealii::EvaluationFlags::EvaluationFlags evaluation_flags = - dealii::EvaluationFlags::nothing; - dealii::EvaluationFlags::EvaluationFlags residual_flags = - dealii::EvaluationFlags::nothing; -}; - -#endif /* INCLUDE_MODELVARIABLE_H_ */ diff --git a/include/core/parse_cmd_options.h b/include/core/parse_cmd_options.h new file mode 100644 index 000000000..ee92c97bc --- /dev/null +++ b/include/core/parse_cmd_options.h @@ -0,0 +1,101 @@ +#ifndef parse_cmd_options_h +#define parse_cmd_options_h + +#include + +#include +#include +#include +#include +#include + +class parseCMDOptions +{ +public: + parseCMDOptions(int &_argc, char **argv) + : argc(_argc) + { + for (int i = 1; i < argc; ++i) + { + tokens.emplace_back(argv[i]); + } + } + + std::string + getParametersFilename() + { + // Check that there aren't too many arguments + if (argc > 3) + { + throw std::runtime_error( + "Too many command line arguments. The only argument should specify " + "the input file name. For example, `./main -i parameters.prm`"); + } + + // Default filename + std::string parameters_filename = "parameters.prm"; + + if (argc >= 2) + { + if (!cmdOptionExists("-i")) + { + throw std::runtime_error( + "Invalid command line option. Use `-i` to specify the input file, " + "e.g., `./main -i parameters.prm`."); + } + + // If -i is provided, check for an argument (argc == 3) + if (argc == 3) + { + parameters_filename = getCmdOption("-i"); + } + } + + // Validate the parameters file (i.e., it ends in .prm) + if (parameters_filename.size() < 4 || + parameters_filename.substr(parameters_filename.size() - 4) != ".prm") + { + throw std::runtime_error("The input file must have the `.prm` extension. Please " + "rename the file accordingly."); + } + + // Check if the file exists + std::ifstream ifs_prm(parameters_filename); + if (!ifs_prm) + { + throw std::runtime_error("The specified parameters file `" + parameters_filename + + "` does not exist."); + } + + // Log the filename being used + conditionalOStreams::pout_base + << "Using the input parameter file: " << parameters_filename << '\n'; + + return parameters_filename; + } + +private: + int argc; + std::vector tokens; + + [[nodiscard]] const std::string & + getCmdOption(const std::string &option) const + { + std::vector::const_iterator itr; + itr = std::find(tokens.begin(), tokens.end(), option); + if (itr != tokens.end() && ++itr != tokens.end()) + { + return *itr; + } + static const std::string empty_string; + return empty_string; + } + + [[nodiscard]] bool + cmdOptionExists(const std::string &option) const + { + return std::find(tokens.begin(), tokens.end(), option) != tokens.end(); + } +}; + +#endif diff --git a/include/core/pde_problem.h b/include/core/pde_problem.h new file mode 100644 index 000000000..fb7201ce1 --- /dev/null +++ b/include/core/pde_problem.h @@ -0,0 +1,157 @@ +#ifndef pde_problem_h +#define pde_problem_h + +#include +#include +#include +#include +#include +#include +#include + +/** + * Forward declaration for user-implemented PDE class. + */ +template +class customPDE; + +/** + * \brief This is the main class that handles the construction and solving of + * user-specified PDEs. + */ +template +class PDEProblem +{ +public: + /** + * \brief Constructor. + */ + PDEProblem(const userInputParameters &_user_inputs); + + /** + * \brief Run initialization and solving steps of the given problem. + */ + void + run(); + +private: + /** + * \brief Main time-stepping loop that calls solve_increment, reinit_system, + * output_results, etc... + */ + void + solve(); + + /** + * \brief Solve a single increment of the given PDEs. + */ + void + solve_increment(); + + /** + * \brief Initialize the system. + */ + void + init_system(); + + /** + * \brief Reinitialize the system. + */ + void + reinit_system(); + + /** + * \brief The field index we are solving. + */ + const uint field_index = 0; + + /** + * \brief User-inputs. + */ + const userInputParameters &user_inputs; + + /** + * \brief Triangulation handler. + */ + triangulationHandler triangulation_handler; + + /** + * \brief The solution vector. + */ + dealii::LinearAlgebra::distributed::Vector solution; + + /** + * \brief Triangulation DoFs. + */ + dealii::DoFHandler dof_handler; + + /** + * \brief Solver that we use for the linear solve + */ + std::unique_ptr> solver; +}; + +template +PDEProblem::PDEProblem(const userInputParameters &_user_inputs) + : user_inputs(_user_inputs) +{ + if (user_inputs.linear_solve_parameters.linear_solve.at(field_index).preconditioner == + preconditionerType::GMG) + { + solver = std::make_unique>(user_inputs, field_index); + } + else + { + solver = std::make_unique>(user_inputs, field_index); + } +} + +template +void +PDEProblem::init_system() +{ + solver->init_system(triangulation_handler, solution, dof_handler); +} + +template +void +PDEProblem::solve_increment() +{ + solver->solve(triangulation_handler, solution, dof_handler); +} + +template +void +PDEProblem::solve() +{ + triangulation_handler.generate_mesh(user_inputs); + + init_system(); + solve_increment(); + + solution.update_ghost_values(); + solutionOutput output(solution, dof_handler, degree); +} + +template +void +PDEProblem::run() +{ + // Print information regarding the vectorized array lanes to summary.log + const uint n_vect_doubles = dealii::VectorizedArray::size(); + const uint n_vect_bits = 8 * sizeof(double) * n_vect_doubles; + + conditionalOStreams::pout_summary() + << "================================================\n" + << "\tSolve\n" + << "================================================\n" + << "Vectorization over " << n_vect_doubles << " doubles = " << n_vect_bits + << " bits (" << Utilities::System::get_current_vectorization_level() << ')' << '\n'; + + // Solve solution + solve(); + + // Print summary of timing information to summary.log +} + +#endif \ No newline at end of file diff --git a/include/core/solvers/solvers.h b/include/core/postprocessing/postprocessing.h similarity index 100% rename from include/core/solvers/solvers.h rename to include/core/postprocessing/postprocessing.h diff --git a/include/core/refinement/AdaptiveRefinement.h b/include/core/refinement/AdaptiveRefinement.h deleted file mode 100644 index fad85f842..000000000 --- a/include/core/refinement/AdaptiveRefinement.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef INCLUDE_ADAPTIVEREFINEMENT_H_ -#define INCLUDE_ADAPTIVEREFINEMENT_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace dealii; - -/** - * A class that handles the determination and application of AMR criterion. - */ -template -class AdaptiveRefinement -{ -public: - /** - * Default constructor. - */ - AdaptiveRefinement( - const userInputParameters &_userInputs, - parallel::distributed::Triangulation &_triangulation, - std::vector> &_fields, - std::vector *> &_solutionSet, - std::vector> *> &_soltransSet, - std::vector *> &_FESet, - std::vector *> &_dofHandlersSet_nonconst, - std::vector *> &_constraintsDirichletSet, - std::vector *> &_constraintsOtherSet); - - /** - * Perform the adaptive refinement based on the specified AMR criterion. Also apply - * constraints when in the 0th timestep. - */ - void - do_adaptive_refinement(unsigned int _currentIncrement); - - /** - * Refine the triangulation and transfer the solution. - */ - void - refine_grid(); - -protected: - /** - * Mark cells to be coarsened or refined based on the specified AMR criterion. - */ - void - adaptive_refinement_criterion(); - -private: - userInputParameters userInputs; - - parallel::distributed::Triangulation &triangulation; - - std::vector> &fields; - - std::vector *> &solutionSet; - - std::vector> *> &soltransSet; - - std::vector *> &FESet; - - std::vector *> &dofHandlersSet_nonconst; - - std::vector *> &constraintsDirichletSet; - - std::vector *> &constraintsOtherSet; -}; - -#endif \ No newline at end of file diff --git a/include/core/refinement/RefinementCriterion.h b/include/core/refinement/RefinementCriterion.h deleted file mode 100644 index ec4d098cd..000000000 --- a/include/core/refinement/RefinementCriterion.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef INCLUDE_REFINMENTCRITERION_H_ -#define INCLUDE_REFINMENTCRITERION_H_ - -enum RefinementCriterionFlags -{ - criterion_default = 0, - criterion_value = 0x0001, - criterion_gradient = 0x0002 -}; - -// Function that enables bitwise OR between flags -inline RefinementCriterionFlags -operator|(const RefinementCriterionFlags flag_1, const RefinementCriterionFlags flag_2) -{ - return static_cast(static_cast(flag_1) | - static_cast(flag_2)); -} - -// Function that enables bitwise compound OR between flags -inline RefinementCriterionFlags & -operator|=(RefinementCriterionFlags &flag_1, const RefinementCriterionFlags flag_2) -{ - flag_1 = flag_1 | flag_2; - return flag_1; -} - -// Function that enables bitwise AND between flags -inline RefinementCriterionFlags -operator&(const RefinementCriterionFlags flag_1, const RefinementCriterionFlags flag_2) -{ - return static_cast(static_cast(flag_1) & - static_cast(flag_2)); -} - -// Function that enables bitwise compound AND between flags -inline RefinementCriterionFlags & -operator&=(RefinementCriterionFlags &flag_1, const RefinementCriterionFlags flag_2) -{ - flag_1 = flag_1 & flag_2; - return flag_1; -} - -/** - * This class holds information for a determining whether the mesh should be - * refined. - */ -class RefinementCriterion -{ -public: - std::string variable_name; - unsigned int variable_index; - RefinementCriterionFlags criterion_type; - double value_lower_bound; - double value_upper_bound; - double gradient_lower_bound; -}; - -#endif /* INCLUDE_REFINMENTCRITERION_H_ */ diff --git a/include/core/triangulation.h b/include/core/refinement/adaptive_refinement.h similarity index 100% rename from include/core/triangulation.h rename to include/core/refinement/adaptive_refinement.h diff --git a/include/core/refinement/refinement_criterion.h b/include/core/refinement/refinement_criterion.h new file mode 100644 index 000000000..821a2ca91 --- /dev/null +++ b/include/core/refinement/refinement_criterion.h @@ -0,0 +1,89 @@ +#ifndef refinement_criterion_h +#define refinement_criterion_h + +#include +#include +#include + +enum RefinementCriterionFlags : std::uint8_t +{ + criterion_default = 0, + criterion_value = 0x0001, + criterion_gradient = 0x0002 +}; + +// Function that enables bitwise OR between flags +inline RefinementCriterionFlags +operator|(const RefinementCriterionFlags flag_1, const RefinementCriterionFlags flag_2) +{ + return static_cast(static_cast(flag_1) | + static_cast(flag_2)); +} + +// Function that enables bitwise compound OR between flags +inline RefinementCriterionFlags & +operator|=(RefinementCriterionFlags &flag_1, const RefinementCriterionFlags flag_2) +{ + flag_1 = flag_1 | flag_2; + return flag_1; +} + +// Function that enables bitwise AND between flags +inline RefinementCriterionFlags +operator&(const RefinementCriterionFlags flag_1, const RefinementCriterionFlags flag_2) +{ + return static_cast(static_cast(flag_1) & + static_cast(flag_2)); +} + +// Function that enables bitwise compound AND between flags +inline RefinementCriterionFlags & +operator&=(RefinementCriterionFlags &flag_1, const RefinementCriterionFlags flag_2) +{ + flag_1 = flag_1 & flag_2; + return flag_1; +} + +/** + * This class holds information for a determining whether the mesh should be + * refined. + */ +class RefinementCriterion +{ +public: + std::string variable_name; + uint variable_index = 0; + RefinementCriterionFlags criterion_type = RefinementCriterionFlags::criterion_default; + double value_lower_bound = DBL_MAX; + double value_upper_bound = DBL_MAX; + double gradient_lower_bound = DBL_MAX; + + /** + * \brief Convert refinement criterion type to string. + */ + [[nodiscard]] std::string + criterion_to_string() const + { + if (criterion_type == RefinementCriterionFlags::criterion_default) + { + return "None"; + } + if (((criterion_type & RefinementCriterionFlags::criterion_value) != 0U) && + ((criterion_type & RefinementCriterionFlags::criterion_gradient) != 0U)) + { + return "Value and gradient"; + } + if ((criterion_type & RefinementCriterionFlags::criterion_value) != 0U) + { + return "Value"; + } + if ((criterion_type & RefinementCriterionFlags::criterion_gradient) != 0U) + { + return "Gradient"; + } + + return "Unknown criterion"; + } +}; + +#endif diff --git a/include/core/solution_output.h b/include/core/solution_output.h new file mode 100644 index 000000000..b498bd3c5 --- /dev/null +++ b/include/core/solution_output.h @@ -0,0 +1,50 @@ +#ifndef solution_output_h +#define solution_output_h + +#include +#include +#include + +/** + * \brief Class that outputs a passed solution to vtu, vtk, or pvtu + */ +template +class solutionOutput +{ +public: + /** + * \brief Constructor for a single field that must be output. + */ + solutionOutput(const dealii::LinearAlgebra::distributed::Vector &solution, + const dealii::DoFHandler &dof_handler, + const uint °ree); + +private: +}; + +template +solutionOutput::solutionOutput( + const dealii::LinearAlgebra::distributed::Vector &solution, + const dealii::DoFHandler &dof_handler, + const uint °ree) +{ + // Init data out + dealii::DataOut data_out; + + // Add data vector + data_out.add_data_vector(dof_handler, solution, "solution"); + + // Build patches to linearly interpolate from higher order element degrees. Note that + // this essentially converts the element to an equal amount of subdivisions in the + // output. This does not make subdivisions and element degree equivalent in the + // simulation! + data_out.build_patches(degree); + + // Set some flags for data output + dealii::DataOutBase::VtkFlags flags; + flags.compression_level = dealii::DataOutBase::CompressionLevel::best_speed; + data_out.set_flags(flags); + data_out.write_vtu_with_pvtu_record("./", "solution", 0, MPI_COMM_WORLD, 3); +} + +#endif \ No newline at end of file diff --git a/include/core/solvers/SolverParameters.h b/include/core/solvers/SolverParameters.h deleted file mode 100644 index 0d38528ae..000000000 --- a/include/core/solvers/SolverParameters.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef INCLUDE_NONLINEARSOLVERPARAMETERS_H_ -#define INCLUDE_NONLINEARSOLVERPARAMETERS_H_ - -#include - -enum SolverToleranceType -{ - ABSOLUTE_RESIDUAL, - RELATIVE_RESIDUAL_CHANGE, - ABSOLUTE_SOLUTION_CHANGE -}; - -/** - * This is a base class that holds parameters related to a numerical solver - * (either linear or nonlinear) As in many other PRISMS-PF classes, access to - * the parameters for the separate governing equations is specified via the - * global variable index. Requests for solver parameters for inappropriate - * governing equations return errors. - */ -class SolverParametersBase -{ -public: - /** - * Method to get the tolerance type for one of the governing equations. - */ - SolverToleranceType - getToleranceType(unsigned int index); - - /** - * Method to get the tolerance value for one of the governing equations. - */ - double - getToleranceValue(unsigned int index); - -protected: - std::vector var_index_list; - - std::vector tolerance_type_list; - std::vector tolerance_value_list; - - /** - * Method to get the internal nonlinearSolverParameters index from the global - * variable index. - */ - unsigned int - getEquationIndex(unsigned int global_index); -}; - -/** - * This class holds all of the parameters for the linear solver. Requests for - * linear solver parameters for governing equations without a matrix inversion - * return errors. - */ -class LinearSolverParameters : public SolverParametersBase -{ -public: - /** - * Method to load the parameters for one governing equation into the class. - * This must be defined for all classes derived from SolverParametersBase. - */ - void - loadParameters(unsigned int _var_index, - SolverToleranceType _tolerance_type, - double _tolerance_value, - unsigned int _max_iterations); - - /** - * Method to get the maximum number of allowed iterations for the linear - * solver. - */ - unsigned int - getMaxIterations(unsigned int index); - -protected: - std::vector max_iterations_list; -}; - -/** - * This class holds all of the parameters for the nonlinear solver. Requests for - * nonlinear solver parameters for non-nonlinear governing equations return - * errors. - */ -class NonlinearSolverParameters : public SolverParametersBase -{ -public: - /** - * Method to get the maximum number of allowed iterations for the nonlinear - * solver. - */ - void - setMaxIterations(unsigned int _max_iterations); - - /** - * Method to get the maximum number of allowed iterations for the nonlinear - * solver. - */ - [[nodiscard]] double - getMaxIterations() const; - - /** - * Method to load the parameters for one governing equation into the class. - * This must be defined for all classes derived from SolverParametersBase. - */ - void - loadParameters(unsigned int _var_index, - SolverToleranceType _tolerance_type, - double _tolerance_value, - bool _backtrack_damping_flag, - double _backtrack_step_modifier, - double _backtrack_residual_decrease_coeff, - double _default_dampling_coefficient, - bool _laplace_for_initial_guess); - - /** - * Method to get the backtrack line-search damping flag for one of the - * governing equations. - */ - bool - getBacktrackDampingFlag(unsigned int index); - - /** - * Method to get the backtrack line-search damping step size modifier for one - * of the governing equations. - */ - double - getBacktrackStepModifier(unsigned int index); - - /** - * Method to get the backtrack line-search damping residual decrease - * coefficient for one of the governing equations. - */ - double - getBacktrackResidualDecreaseCoeff(unsigned int index); - - /** - * Method to get the default damping coefficient for one of the governing - * equations. - */ - double - getDefaultDampingCoefficient(unsigned int index); - - /** - * Method to get the flag determining if the solution to Laplace's equation - * should be used as the initial guess for one of the governing equations. - */ - bool - getLaplaceInitializationFlag(unsigned int index); - -private: - unsigned int max_iterations; - - std::vector backtrack_damping_flag_list; - std::vector backtrack_step_modifier_list; - std::vector backtrack_residual_decrease_coeff_list; - std::vector default_damping_coefficient_list; - std::vector laplace_for_initial_guess_list; -}; - -#endif diff --git a/include/core/solvers/base_solver.h b/include/core/solvers/base_solver.h new file mode 100644 index 000000000..3a494ec2a --- /dev/null +++ b/include/core/solvers/base_solver.h @@ -0,0 +1,145 @@ +#ifndef base_solver_h +#define base_solver_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * Forward declaration for user-implemented PDE class. + */ +template +class customPDE; + +/** + * \brief Class that handles the assembly and solving of a field with the identity + * preconditioner (no preconditioner) + */ +template +class baseSolver +{ +public: + using SystemMatrixType = customPDE; + + /** + * \brief Constructor. + */ + baseSolver(const userInputParameters &_user_inputs, const uint &_field_index); + + /** + * \brief Destructor. + */ + ~baseSolver() = default; + + /** + * \brief Initialize the system. + */ + virtual void + init_system(const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + dealii::DoFHandler &dof_handler) = 0; + + /** + * \brief Reinitialize the system. + */ + virtual void + reinit_system() = 0; + + /** + * \brief Solve the system Ax=b. + */ + virtual void + solve(const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + const dealii::DoFHandler &dof_handler) = 0; + +protected: + /** + * \brief Compute the solver tolerance based on the specified tolerance type. + */ + void + compute_solver_tolerance(); + + /** + * \brief The field index we are solving. + */ + const uint field_index; + + /** + * \brief User-inputs. + */ + const userInputParameters &user_inputs; + + /** + * \brief Constraint handler. + */ + constraintHandler constraint_handler; + + /** + * \brief The change in solution that is solved for to update the solution vector. This + * is the x in Ax=b. + */ + dealii::LinearAlgebra::distributed::Vector change_in_solution; + + /** + * \brief The residual vector. This is the b in Ax=b. + */ + dealii::LinearAlgebra::distributed::Vector residual; + + /** + * \brief PDE operator. + */ + SystemMatrixType system_matrix; + + /** + * \brief Finite element rule. + */ + const dealii::FE_Q fe; + + /** + * \brief Mappings to and from reference cell. + */ + dealii::MappingQ1 mapping; + + /** + * \brief Solver control. + */ + dealii::SolverControl solver_control; + + /** + * \brief Solver tolerance + */ + double tolerance = 0.0; +}; + +template +baseSolver::baseSolver(const userInputParameters &_user_inputs, + const uint &_field_index) + : field_index(_field_index) + , user_inputs(_user_inputs) + , constraint_handler(_user_inputs.boundary_parameters, _field_index) + , system_matrix(_user_inputs) + , fe(degree) + , solver_control( + _user_inputs.linear_solve_parameters.linear_solve.at(_field_index).max_iterations) +{} + +template +inline void +baseSolver::compute_solver_tolerance() +{ + tolerance = + user_inputs.linear_solve_parameters.linear_solve.at(field_index).tolerance_type == + solverToleranceType::RELATIVE_RESIDUAL_CHANGE + ? user_inputs.linear_solve_parameters.linear_solve.at(field_index).tolerance * + residual.l2_norm() + : user_inputs.linear_solve_parameters.linear_solve.at(field_index).tolerance; +} + +#endif \ No newline at end of file diff --git a/include/core/solvers/explicit_constant_solver.h b/include/core/solvers/explicit_constant_solver.h new file mode 100644 index 000000000..7bb65815c --- /dev/null +++ b/include/core/solvers/explicit_constant_solver.h @@ -0,0 +1,10 @@ +#ifndef explicit_constant_solver_h +#define explicit_constant_solver_h + +/** + * \brief This class handles the explicit solves of all constant fields + */ +class explicitConstantSolver +{}; + +#endif \ No newline at end of file diff --git a/include/core/solvers/explicit_postprocess_solver.h b/include/core/solvers/explicit_postprocess_solver.h new file mode 100644 index 000000000..1c110244a --- /dev/null +++ b/include/core/solvers/explicit_postprocess_solver.h @@ -0,0 +1,10 @@ +#ifndef explicit_postprocess_solver_h +#define explicit_postprocess_solver_h + +/** + * \brief This class handles the explicit solves of all postprocessed fields + */ +class explicitPostprocessSolver +{}; + +#endif \ No newline at end of file diff --git a/include/core/solvers/explicit_solver.h b/include/core/solvers/explicit_solver.h new file mode 100644 index 000000000..73d3da2ff --- /dev/null +++ b/include/core/solvers/explicit_solver.h @@ -0,0 +1,39 @@ +#ifndef explicit_solver_h +#define explicit_solver_h + +/** + * \brief This class handles the explicit solves of all explicit fields + */ +class explicitSolver +{ +public: + /** + * \brief Constructor. + */ + explicitSolver(); + + /** + * \brief Destructor. + */ + ~explicitSolver(); + + /** + * \brief Initialize system. + */ + void + init_system(); + + /** + * \brief Reinitialize system. + */ + void + reinit_system(); + + /** + * \brief Solve a single update step. + */ + void + solve(); +}; + +#endif \ No newline at end of file diff --git a/include/core/solvers/gmg_solver.h b/include/core/solvers/gmg_solver.h new file mode 100644 index 000000000..10207e750 --- /dev/null +++ b/include/core/solvers/gmg_solver.h @@ -0,0 +1,305 @@ +#ifndef gmg_solver_h +#define gmg_solver_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * Forward declaration for user-implemented PDE class. + */ +template +class customPDE; + +/** + * \brief Class that handles the assembly and solving of a field with a GMG preconditioner + */ +template +class GMGSolver : public baseSolver +{ +public: + using SystemMatrixType = customPDE; + + using LevelMatrixType = customPDE; + + /** + * \brief Constructor. + */ + GMGSolver(const userInputParameters &_user_inputs, const uint &_field_index); + + /** + * \brief Destructor. + */ + ~GMGSolver() = default; + + /** + * \brief Initialize the system. + */ + void + init_system(const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + dealii::DoFHandler &dof_handler) override; + + /** + * \brief Reinitialize the system. + */ + void + reinit_system() override; + + /** + * \brief Solve the system Ax=b. + */ + void + solve(const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + const dealii::DoFHandler &dof_handler) override; + +private: + /** + * \brief PDE operator for each multigrid level. + */ + dealii::MGLevelObject mg_matrices; + + /** + * \brief Boundary constraints for multigrid levels. + */ + dealii::MGConstrainedDoFs mg_constrained_dofs; + + /** + * \brief Set of boundary ids that are dirichlet constraints + */ + std::set dirichlet_boundary_ids; +}; + +template +GMGSolver::GMGSolver(const userInputParameters &_user_inputs, + const uint &_field_index) + : baseSolver(_user_inputs, _field_index) + , mg_matrices(dealii::MGLevelObject(0, 0, _user_inputs)) +{ + // Get the dirichlet boundary ids for multigrid constriants + for (const auto &[component, boundary_condition] : + this->user_inputs.boundary_parameters.boundary_condition_list.at( + this->field_index)) + { + Assert(component == 0, FeatureNotImplemented("Vector multigrid")); + + for (const auto &[boundary_id, boundary_type] : + boundary_condition.boundary_condition_map) + { + if (boundary_type == boundaryType::DIRICHLET || + boundary_type == boundaryType::NON_UNIFORM_DIRICHLET) + { + dirichlet_boundary_ids.insert(boundary_id); + } + } + } +} + +template +inline void +GMGSolver::init_system( + const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + dealii::DoFHandler &dof_handler) +{ + this->system_matrix.clear(); + mg_matrices.clear_elements(); + + dof_handler.reinit(triangulation_handler.get_triangulation()); + dof_handler.distribute_dofs(this->fe); + dof_handler.distribute_mg_dofs(); + + this->constraint_handler.make_constraints(this->mapping, dof_handler); + + typename dealii::MatrixFree::AdditionalData additional_data; + additional_data.tasks_parallel_scheme = + dealii::MatrixFree::AdditionalData::none; + + // This should be done according to the residual flags to prevent excess data being + // evaluated for the shape functions. + additional_data.mapping_update_flags = + (update_values | update_gradients | update_JxW_values | update_quadrature_points); + std::shared_ptr> system_mf_storage( + new dealii::MatrixFree()); + system_mf_storage->reinit(this->mapping, + dof_handler, + this->constraint_handler.get_constraints(), + dealii::QGauss<1>(this->fe.degree + 1), + additional_data); + this->system_matrix.initialize(system_mf_storage); + + this->system_matrix.initialize_dof_vector(this->change_in_solution); + this->system_matrix.initialize_dof_vector(solution); + this->system_matrix.initialize_dof_vector(this->residual); + + // Apply boundary conditions to the solution vector as an initial guess + this->constraint_handler.get_constraints().distribute(solution); + + const uint nlevels = triangulation_handler.get_n_global_levels(); + mg_matrices.resize(0, nlevels - 1, this->user_inputs); + + mg_constrained_dofs.initialize(dof_handler); + mg_constrained_dofs.make_zero_boundary_constraints(dof_handler, dirichlet_boundary_ids); + + for (uint level = 0; level < nlevels; ++level) + { + // Setup the constraint set for the multigrid level + dealii::AffineConstraints level_constraints( + dof_handler.locally_owned_mg_dofs(level), + dealii::DoFTools::extract_locally_relevant_level_dofs(dof_handler, level)); + + // Loop through boundary indices and apply constraints + for (const dealii::types::global_dof_index dof_index : + mg_constrained_dofs.get_boundary_indices(level)) + { + level_constraints.constrain_dof_to_zero(dof_index); + } + level_constraints.close(); + + typename dealii::MatrixFree::AdditionalData additional_data; + additional_data.tasks_parallel_scheme = + dealii::MatrixFree::AdditionalData::none; + + // This should be done according to the residual flags to prevent excess data being + // evaluated for the shape functions. + additional_data.mapping_update_flags = + (update_values | update_gradients | update_JxW_values | update_quadrature_points); + additional_data.mg_level = level; + std::shared_ptr> mg_mf_storage_level = + std::make_shared>(); + mg_mf_storage_level->reinit(this->mapping, + dof_handler, + level_constraints, + dealii::QGauss<1>(this->fe.degree + 1), + additional_data); + + mg_matrices[level].initialize(mg_mf_storage_level, mg_constrained_dofs, level); + } + + conditionalOStreams::pout_base + << "Number of degrees of freedom: " << dof_handler.n_dofs() << std::endl; + conditionalOStreams::pout_summary() + << "Number of degrees of freedom: " << dof_handler.n_dofs() << std::endl; +} + +template +inline void +GMGSolver::reinit_system() +{} + +template +inline void +GMGSolver::solve( + const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + const dealii::DoFHandler &dof_handler) +{ + dealii::MGTransferMatrixFree mg_transfer(mg_constrained_dofs); + mg_transfer.build(dof_handler); + + using SmootherType = + dealii::PreconditionChebyshev>; + dealii::mg::SmootherRelaxation> + mg_smoother; + dealii::MGLevelObject smoother_data; + smoother_data.resize(0, triangulation_handler.get_n_global_levels() - 1); + for (uint level = 0; level < triangulation_handler.get_n_global_levels(); ++level) + { + if (level > 0) + { + smoother_data[level].smoothing_range = + this->user_inputs.linear_solve_parameters.linear_solve.at(this->field_index) + .smoothing_range; + smoother_data[level].degree = + this->user_inputs.linear_solve_parameters.linear_solve.at(this->field_index) + .smoother_iterations; + smoother_data[level].eig_cg_n_iterations = + this->user_inputs.linear_solve_parameters.linear_solve.at(this->field_index) + .eig_cg_n_iterations; + } + else + { + smoother_data[0].smoothing_range = 1e-3; + smoother_data[0].degree = dealii::numbers::invalid_unsigned_int; + smoother_data[0].eig_cg_n_iterations = mg_matrices[0].m(); + } + mg_matrices[level].compute_diagonal(); + smoother_data[level].preconditioner = + mg_matrices[level].get_matrix_diagonal_inverse(); + } + mg_smoother.initialize(mg_matrices, smoother_data); + + dealii::MGCoarseGridApplySmoother> + mg_coarse; + mg_coarse.initialize(mg_smoother); + + dealii::mg::Matrix> mg_matrix( + mg_matrices); + + dealii::MGLevelObject> + mg_interface_matrices; + mg_interface_matrices.resize(0, triangulation_handler.get_n_global_levels() - 1); + for (uint level = 0; level < triangulation_handler.get_n_global_levels(); ++level) + { + mg_interface_matrices[level].initialize(mg_matrices[level]); + } + dealii::mg::Matrix> mg_interface( + mg_interface_matrices); + + Multigrid> mg(mg_matrix, + mg_coarse, + mg_transfer, + mg_smoother, + mg_smoother); + mg.set_edge_matrices(mg_interface, mg_interface); + + dealii::PreconditionMG, + dealii::MGTransferMatrixFree> + preconditioner(dof_handler, mg, mg_transfer); + + // Compute the residual of the current solution (Ax-b) + this->system_matrix.compute_residual(this->residual, solution); + + // Determine the residual tolerance + this->compute_solver_tolerance(); + + // Update solver controls + this->solver_control.set_tolerance(this->tolerance); + dealii::SolverCG> cg( + this->solver_control); + + try + { + this->change_in_solution = 0.0; + cg.solve(this->system_matrix, + this->change_in_solution, + this->residual, + preconditioner); + this->constraint_handler.get_constraints().set_zero(this->change_in_solution); + } + catch (...) + { + conditionalOStreams::pout_base + << "Warning: linear solver did not converge as per set tolerances.\n"; + } + solution += this->change_in_solution; + this->constraint_handler.get_constraints().distribute(solution); + + conditionalOStreams::pout_summary() + << this->solver_control.last_step() << " iterations\n"; +} + +#endif \ No newline at end of file diff --git a/include/core/solvers/identity_solver.h b/include/core/solvers/identity_solver.h new file mode 100644 index 000000000..529e07908 --- /dev/null +++ b/include/core/solvers/identity_solver.h @@ -0,0 +1,153 @@ +#ifndef identity_solver_h +#define identity_solver_h + +#include +#include +#include + +#include + +/** + * Forward declaration for user-implemented PDE class. + */ +template +class customPDE; + +/** + * \brief Class that handles the assembly and solving of a field with the identity + * preconditioner (no preconditioner) + */ +template +class identitySolver : public baseSolver +{ +public: + using SystemMatrixType = customPDE; + + /** + * \brief Constructor. + */ + identitySolver(const userInputParameters &_user_inputs, const uint &_field_index); + + /** + * \brief Destructor. + */ + ~identitySolver() = default; + + /** + * \brief Initialize the system. + */ + void + init_system(const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + dealii::DoFHandler &dof_handler) override; + + /** + * \brief Reinitialize the system. + */ + void + reinit_system() override; + + /** + * \brief Solve the system Ax=b. + */ + void + solve(const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + const dealii::DoFHandler &dof_handler) override; +}; + +template +identitySolver::identitySolver(const userInputParameters &_user_inputs, + const uint &_field_index) + : baseSolver(_user_inputs, _field_index) +{} + +template +inline void +identitySolver::init_system( + const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + dealii::DoFHandler &dof_handler) +{ + this->system_matrix.clear(); + + dof_handler.reinit(triangulation_handler.get_triangulation()); + dof_handler.distribute_dofs(this->fe); + + this->constraint_handler.make_constraints(this->mapping, dof_handler); + + typename dealii::MatrixFree::AdditionalData additional_data; + additional_data.tasks_parallel_scheme = + dealii::MatrixFree::AdditionalData::none; + + // This should be done according to the residual flags to prevent excess data being + // evaluated for the shape functions. + additional_data.mapping_update_flags = + (update_values | update_gradients | update_JxW_values | update_quadrature_points); + std::shared_ptr> system_mf_storage( + new dealii::MatrixFree()); + system_mf_storage->reinit(this->mapping, + dof_handler, + this->constraint_handler.get_constraints(), + dealii::QGauss<1>(this->fe.degree + 1), + additional_data); + this->system_matrix.initialize(system_mf_storage); + + this->system_matrix.initialize_dof_vector(this->change_in_solution); + this->system_matrix.initialize_dof_vector(solution); + this->system_matrix.initialize_dof_vector(this->residual); + + // Apply boundary conditions to the solution vector as an initial guess + this->constraint_handler.get_constraints().distribute(solution); + + conditionalOStreams::pout_base + << "Number of degrees of freedom: " << dof_handler.n_dofs() << std::endl; + conditionalOStreams::pout_summary() + << "Number of degrees of freedom: " << dof_handler.n_dofs() << std::endl; +} + +template +inline void +identitySolver::reinit_system() +{} + +template +inline void +identitySolver::solve( + [[maybe_unused]] const triangulationHandler &triangulation_handler, + dealii::LinearAlgebra::distributed::Vector &solution, + [[maybe_unused]] const dealii::DoFHandler &dof_handler) +{ + // Compute the residual of the current solution (Ax-b) + this->system_matrix.compute_residual(this->residual, solution); + + // Determine the residual tolerance + this->compute_solver_tolerance(); + + // Setup solver controls + this->solver_control.set_tolerance(this->tolerance); + dealii::SolverCG> cg( + this->solver_control); + + try + { + this->change_in_solution = 0.0; + cg.solve(this->system_matrix, + this->change_in_solution, + this->residual, + PreconditionIdentity()); + this->constraint_handler.get_constraints().set_zero(this->change_in_solution); + } + catch (...) + { + conditionalOStreams::pout_base + << "Warning: linear solver did not converge as per set tolerances.\n"; + } + solution += this->change_in_solution; + this->constraint_handler.get_constraints().distribute(solution); + + conditionalOStreams::pout_summary() + << this->solver_control.last_step() << " iterations\n"; +} + +#endif \ No newline at end of file diff --git a/include/core/solvers/nonexplicit_auxiliary_solver.h b/include/core/solvers/nonexplicit_auxiliary_solver.h new file mode 100644 index 000000000..24fbe61c6 --- /dev/null +++ b/include/core/solvers/nonexplicit_auxiliary_solver.h @@ -0,0 +1,10 @@ +#ifndef nonexplicit_auxiliary_solver_h +#define nonexplicit_auxiliary_solver_h + +/** + * \brief This class handles the explicit solves of a single nonexplicit field + */ +class nonexplicitAuxiliarySolves +{}; + +#endif \ No newline at end of file diff --git a/include/core/solvers/nonexplicit_co_nonlinear_solver.h b/include/core/solvers/nonexplicit_co_nonlinear_solver.h new file mode 100644 index 000000000..dae004fca --- /dev/null +++ b/include/core/solvers/nonexplicit_co_nonlinear_solver.h @@ -0,0 +1,10 @@ +#ifndef nonexplicit_co_nonlinear_solver_h +#define nonexplicit_co_nonlinear_solver_h + +/** + * \brief This class handles the nonlinear solves of a several nonexplicit fields + */ +class nonexplicitCoNonlinearSolver +{}; + +#endif \ No newline at end of file diff --git a/include/core/solvers/nonexplicit_linear_solver.h b/include/core/solvers/nonexplicit_linear_solver.h new file mode 100644 index 000000000..3353f1bb8 --- /dev/null +++ b/include/core/solvers/nonexplicit_linear_solver.h @@ -0,0 +1,10 @@ +#ifndef nonexplicit_linear_solver_h +#define nonexplicit_linear_solver_h + +/** + * \brief This class handles the linear solves of a single nonexplicit field + */ +class nonexplicitLinearSolver +{}; + +#endif \ No newline at end of file diff --git a/include/core/solvers/nonexplicit_self_nonlinear_solver.h b/include/core/solvers/nonexplicit_self_nonlinear_solver.h new file mode 100644 index 000000000..23f42b9b6 --- /dev/null +++ b/include/core/solvers/nonexplicit_self_nonlinear_solver.h @@ -0,0 +1,10 @@ +#ifndef nonexplicit_self_nonlinear_solver_h +#define nonexplicit_self_nonlinear_solver_h + +/** + * \brief This class handles the self-nonlinear solves of a single nonexplicit field + */ +class nonexplicitSelfNonlinearSolver +{}; + +#endif \ No newline at end of file diff --git a/include/core/triangulation_handler.h b/include/core/triangulation_handler.h new file mode 100644 index 000000000..334716152 --- /dev/null +++ b/include/core/triangulation_handler.h @@ -0,0 +1,220 @@ +#ifndef triangulation_handler_h +#define triangulation_handler_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * \brief This class handlers the generation and manipulation of triangulations. + */ +template +class triangulationHandler +{ +public: + using Triangulation = + typename std::conditional, + dealii::parallel::distributed::Triangulation>::type; + + /** + * \brief Constructor. + */ + triangulationHandler(); + + /** + * \brief Getter function for triangulation (constant reference). + */ + [[nodiscard]] const Triangulation & + get_triangulation() const; + + /** + * \brief Return the global maximum level of the triangulation. + */ + [[nodiscard]] uint + get_n_global_levels() const; + + /** + * \brief Generate mesh. + */ + void + generate_mesh(const userInputParameters &user_inputs); + + /** + * \brief Export triangulation to vtk. + */ + void + export_triangulation_as_vtk(const std::string &filename) const; + +private: + /** + * \brief Mark the domain ids on the triangulation to get the proper mapping of + * specified boundary conditions. + */ + void + mark_boundaries(const userInputParameters &user_inputs) const; + + /** + * \brief Mark certain faces of the triangulation periodic. + */ + void + mark_periodic(const userInputParameters &user_inputs); + + std::unique_ptr triangulation; +}; + +template +triangulationHandler::triangulationHandler() +{ + if constexpr (dim == 1) + { + triangulation = std::make_unique>( + dealii::Triangulation::limit_level_difference_at_vertices); + } + else + { + triangulation = std::make_unique>( + MPI_COMM_WORLD, + dealii::Triangulation::limit_level_difference_at_vertices, + dealii::parallel::distributed::Triangulation::construct_multigrid_hierarchy); + } +} + +template +const typename triangulationHandler::Triangulation & +triangulationHandler::get_triangulation() const +{ + return *triangulation; +} + +template +uint +triangulationHandler::get_n_global_levels() const +{ + return triangulation->n_global_levels(); +} + +template +void +triangulationHandler::generate_mesh(const userInputParameters &user_inputs) +{ + // Generate rectangle + dealii::GridGenerator::subdivided_hyper_rectangle( + *triangulation, + user_inputs.spatial_discretization.subdivisions, + dealii::Point(), + dealii::Point(user_inputs.spatial_discretization.domain_size)); + + // Mark boundaries. This is done before global refinement to reduce the number of cells + // we have to loop through. + mark_boundaries(user_inputs); + + // Mark periodicity + mark_periodic(user_inputs); + + // Output triangulation to vtk if in debug mode +#ifdef DEBUG + export_triangulation_as_vtk("triangulation"); +#endif + + // Global refinement + triangulation->refine_global(user_inputs.spatial_discretization.refine_factor); +} + +template +void +triangulationHandler::export_triangulation_as_vtk(const std::string &filename) const +{ + dealii::GridOut grid_out; + std::ofstream out(filename + ".vtk"); + grid_out.write_vtk(*triangulation, out); + std::cout << "Triangulation written to " << filename << ".vtk\n"; +} + +template +void +triangulationHandler::mark_boundaries( + const userInputParameters &user_inputs) const +{ + double tolerance = 1e-12; + + // Loop through the cells + for (const auto &cell : triangulation->active_cell_iterators()) + { + // Mark the faces (faces_per_cell = 2*dim) + for (uint face_number = 0; face_number < dealii::GeometryInfo::faces_per_cell; + ++face_number) + { + // Direction for quad and hex cells + uint direction = std::floor(face_number / 2); + + // Mark the boundary id for x=0, y=0, z=0 + if (std::fabs(cell->face(face_number)->center()(direction) - 0) < tolerance) + { + cell->face(face_number)->set_boundary_id(face_number); + } + // Mark the boundary id for x=max, y=max, z=max + else if (std::fabs( + cell->face(face_number)->center()(direction) - + (user_inputs.spatial_discretization.domain_size[direction])) < + tolerance) + { + cell->face(face_number)->set_boundary_id(face_number); + } + } + } +} + +template +void +triangulationHandler::mark_periodic(const userInputParameters &user_inputs) +{ + // Add periodicity in the triangulation where specified in the boundary conditions. Note + // that if one field is periodic all others should be as well. + for (const auto &[index, boundary_condition] : + user_inputs.boundary_parameters.boundary_condition_list) + { + for (const auto &[component, condition] : boundary_condition) + { + for (const auto &[boundary_id, boundary_type] : + condition.boundary_condition_map) + { + if (boundary_type == boundaryType::PERIODIC) + { + // Skip boundary ids that are odd since those map to the even faces + if (boundary_id % 2 != 0) + { + continue; + } + + // Create a vector of matched pairs that we fill and enforce upon the + // constaints + std::vector> + periodicity_vector; + + // Determine the direction + const uint direction = std::floor(boundary_id / dim); + + // Collect the matched pairs on the coarsest level of the mesh + dealii::GridTools::collect_periodic_faces(*triangulation, + boundary_id, + boundary_id + 1, + direction, + periodicity_vector); + + // Set constraints + triangulation->add_periodicity(periodicity_vector); + } + } + } + } +} + +#endif \ No newline at end of file diff --git a/include/core/typeDefs.h b/include/core/typeDefs.h deleted file mode 100644 index 732110483..000000000 --- a/include/core/typeDefs.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * typeDefs.h - * - * Created on: Feb 24, 2017 - * Author: stephendewitt - */ - -// #ifndef INCLUDE_TYPEDEFS_H_ -// #define INCLUDE_TYPEDEFS_H_ - -// define FE system types -#ifndef typeScalar -using typeScalar = dealii::FEEvaluation; -#endif -#ifndef typeVector -using typeVector = dealii::FEEvaluation; -#endif -// define data value types -#ifndef scalarvalueType -using scalarvalueType = dealii::VectorizedArray; -#endif -#ifndef vectorvalueType -using vectorvalueType = dealii::Tensor<1, dim, dealii::VectorizedArray>; -#endif -#if problemDIM == 1 -# ifndef scalargradType -using scalargradType = dealii::VectorizedArray; -# endif -# ifndef vectorgradType -using vectorgradType = dealii::VectorizedArray; -# endif -# ifndef vectorhessType -using vectorhessType = dealii::VectorizedArray; -# endif -#else -# ifndef scalargradType -using scalargradType = dealii::Tensor<1, dim, dealii::VectorizedArray>; -# endif -# ifndef scalarhessType -using scalarhessType = dealii::Tensor<2, dim, dealii::VectorizedArray>; -# endif -# ifndef vectorgradType -using vectorgradType = dealii::Tensor<2, dim, dealii::VectorizedArray>; -# endif -# ifndef vectorhessType -using vectorhessType = dealii::Tensor<3, dim, dealii::VectorizedArray>; -# endif -#endif - -// #endif /* INCLUDE_TYPEDEFS_H_ */ diff --git a/include/core/type_enums.h b/include/core/type_enums.h new file mode 100644 index 000000000..904cee436 --- /dev/null +++ b/include/core/type_enums.h @@ -0,0 +1,322 @@ +#ifndef type_enums_h +#define type_enums_h + +#include +#include + +/** + * \brief Type of field. Currently, the only support fields are scalar and vector. + */ +enum fieldType : std::uint8_t +{ + UNDEFINED_FIELD, + SCALAR, + VECTOR +}; + +/** + * \brief Type of PDE that is being solved. + */ +enum PDEType : std::uint8_t +{ + UNDEFINED_PDE, + EXPLICIT_TIME_DEPENDENT, + IMPLICIT_TIME_DEPENDENT, + TIME_INDEPENDENT, + AUXILIARY, + CONSTANT +}; + +/** + * \brief Type of solve. + */ +enum solveType : std::uint8_t +{ + EXPLICIT_RHS, + NONEXPLICIT_RHS, + NONEXPLICIT_LHS, + POSTPROCESS +}; + +/** + * \brief Symmetry of elastic tensor. + */ +enum elasticityModel : std::uint8_t +{ + ISOTROPIC, + TRANSVERSE, + ORTHOTROPIC, + ANISOTROPIC +}; + +/** + * \brief Type of boundary condition + */ +enum boundaryType : std::uint8_t +{ + UNDEFINED_BOUNDARY, + NATURAL, + DIRICHLET, + PERIODIC, + NEUMANN, + NON_UNIFORM_DIRICHLET, + NON_UNIFORM_NEUMANN +}; + +/** + * \brief Internal classification of combined field and solve types. There are six + * different types of solve that are possible. For EXPLICIT solves, all fields of that + * type can be solved concurrently. For NONEXPLICIT_LINEAR, NONEXPLICIT_SELF_NONLINEAR, + * and NONEXPLICIT_AUXILIARY, these must be solved sequentially and wrapped in + * conditionals in the user implmentation. For NONEXPLICIT_CO_NONLINEAR, there are at + * least 2 fields that are nonlinear together, as opposed to NONEXPLICIT_SELF_NONLINEAR, + * which must be solved at the same time. A simply case for this is the steady-state + * Cahn-Hilliard equation. Finally, for EXPLICIT_POSTPROCESS and EXPLICIT_CONSTANT, they + * are more or less the same as EXPLICIT. + */ +enum fieldSolveType : std::uint8_t +{ + UNDEFINED_SOLVE, + EXPLICIT, + NONEXPLICIT_LINEAR, + NONEXPLICIT_SELF_NONLINEAR, + NONEXPLICIT_AUXILIARY, + NONEXPLICIT_CO_NONLINEAR, + EXPLICIT_POSTPROCESS, + EXPLICIT_CONSTANT +}; + +/** + * \brief Internal classification for types of variable dependencies. + */ +enum dependencyType : std::uint8_t +{ + NORMAL, + CHANGE, + OLD_1, + OLD_2, + OLD_3, + OLD_4 +}; + +/** + * \brief Solver tolerance type. + */ +enum solverToleranceType : std::uint8_t +{ + ABSOLUTE_RESIDUAL, + RELATIVE_RESIDUAL_CHANGE +}; + +/** + * \brief Preconditioner type. + */ +enum preconditionerType : std::uint8_t +{ + NONE, + GMG +}; + +/** + * \brief Enum to string for fieldType + */ +inline std::string +to_string(fieldType type) + +{ + switch (type) + { + case fieldType::UNDEFINED_FIELD: + return "UNDEFINED_FIELD"; + case fieldType::SCALAR: + return "SCALAR_FIELD"; + case fieldType::VECTOR: + return "VECTOR_FIELD"; + default: + return "UNKNOWN"; + } +} + +/** + * \brief Enum to string for PDEType + */ +inline std::string +to_string(PDEType type) +{ + switch (type) + { + case PDEType::UNDEFINED_PDE: + return "UNDEFINED_PDE"; + case PDEType::EXPLICIT_TIME_DEPENDENT: + return "EXPLICIT_TIME_DEPENDENT"; + case PDEType::IMPLICIT_TIME_DEPENDENT: + return "IMPLICIT_TIME_DEPENDENT"; + case PDEType::TIME_INDEPENDENT: + return "TIME_INDEPENDENT"; + case PDEType::AUXILIARY: + return "AUXILIARY"; + case PDEType::CONSTANT: + return "CONSTANT"; + default: + return "UNKNOWN"; + } +} + +/** + * \brief Enum to string for solveType + */ +inline std::string +to_string(solveType type) +{ + switch (type) + { + case solveType::EXPLICIT_RHS: + return "EXPLICIT_RHS"; + case solveType::NONEXPLICIT_RHS: + return "NONEXPLICIT_RHS"; + case solveType::NONEXPLICIT_LHS: + return "NONEXPLICIT_LHS"; + case solveType::POSTPROCESS: + return "POSTPROCESS"; + default: + return "UNKNOWN"; + } +} + +/** + * \brief Enum to string for elasticityModel + */ +inline std::string +to_string(elasticityModel type) +{ + switch (type) + { + case elasticityModel::ISOTROPIC: + return "ISOTROPIC"; + case elasticityModel::TRANSVERSE: + return "TRANSVERSE"; + case elasticityModel::ORTHOTROPIC: + return "ORTHOTROPIC"; + case elasticityModel::ANISOTROPIC: + return "ANISOTROPIC"; + default: + return "UNKNOWN"; + } +} + +/** + * \brief Enum to string for boundaryType + */ +inline std::string +to_string(boundaryType type) +{ + switch (type) + { + case boundaryType::UNDEFINED_BOUNDARY: + return "UNDEFINED_BOUNDARY"; + case boundaryType::NATURAL: + return "NATURAL"; + case boundaryType::DIRICHLET: + return "DIRICHLET"; + case boundaryType::PERIODIC: + return "PERIODIC"; + case boundaryType::NEUMANN: + return "NEUMANN"; + case boundaryType::NON_UNIFORM_DIRICHLET: + return "NON_UNIFORM_DIRICHLET"; + case boundaryType::NON_UNIFORM_NEUMANN: + return "NON_UNIFORM_NEUMANN"; + default: + return "UNKNOWN"; + } +} + +/** + * \brief Enum to string for fieldSolveType + */ +inline std::string +to_string(fieldSolveType type) +{ + switch (type) + { + case fieldSolveType::UNDEFINED_SOLVE: + return "UNDEFINED_SOLVE"; + case fieldSolveType::EXPLICIT: + return "EXPLICIT"; + case fieldSolveType::NONEXPLICIT_LINEAR: + return "NONEXPLICIT_LINEAR"; + case fieldSolveType::NONEXPLICIT_SELF_NONLINEAR: + return "NONEXPLICIT_SELF_NONLINEAR"; + case fieldSolveType::NONEXPLICIT_AUXILIARY: + return "NONEXPLICIT_AUXILIARY"; + case fieldSolveType::NONEXPLICIT_CO_NONLINEAR: + return "NONEXPLICIT_CO_NONLINEAR"; + case fieldSolveType::EXPLICIT_POSTPROCESS: + return "EXPLICIT_POSTPROCESS"; + case fieldSolveType::EXPLICIT_CONSTANT: + return "EXPLICIT_CONSTANT"; + default: + return "UNKNOWN"; + } +} + +/** + * \brief Enum to string for dependencyType + */ +inline std::string +to_string(dependencyType type) +{ + switch (type) + { + case dependencyType::NORMAL: + return "NORMAL"; + case dependencyType::CHANGE: + return "CHANGE"; + case dependencyType::OLD_1: + return "OLD_1"; + case dependencyType::OLD_2: + return "OLD_2"; + case dependencyType::OLD_3: + return "OLD_3"; + case dependencyType::OLD_4: + return "OLD_4"; + default: + return "UNKNOWN"; + } +} + +/** + * \brief Enum to string for solverToleranceType + */ +inline std::string +to_string(solverToleranceType type) +{ + switch (type) + { + case solverToleranceType::ABSOLUTE_RESIDUAL: + return "ABSOLUTE_RESIDUAL"; + case solverToleranceType::RELATIVE_RESIDUAL_CHANGE: + return "RELATIVE_RESIDUAL_CHANGE"; + default: + return "UNKNOWN"; + } +} + +/** + * \brief Enum to string for preconditionerType + */ +inline std::string +to_string(preconditionerType type) +{ + switch (type) + { + case preconditionerType::NONE: + return "NONE"; + case preconditionerType::GMG: + return "GMG"; + default: + return "UNKNOWN"; + } +} + +#endif diff --git a/include/core/userInputParameters.h b/include/core/userInputParameters.h deleted file mode 100644 index 5a9886580..000000000 --- a/include/core/userInputParameters.h +++ /dev/null @@ -1,457 +0,0 @@ -// Class to load in the user input from parameters.h and the variable definition -// part of equations.h - -#ifndef INCLUDE_USERINPUTPARAMETERS_H_ -#define INCLUDE_USERINPUTPARAMETERS_H_ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -template -using InputVariant = boost::variant, - dealii::Tensor<2, dim>, - dealii::Tensor<2, 2 * dim - 1 + dim / 3>>; - -enum elasticityModel -{ - ISOTROPIC, - TRANSVERSE, - ORTHOTROPIC, - ANISOTROPIC, - ANISOTROPIC2D -}; - -template -class userInputParameters -{ -public: - /** - * \brief Constructor. Reads in user input parameters from file and loads them into - * member variables. - */ - userInputParameters(inputFileReader &input_file_reader, - dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Creates a list of BCs to store in BC_list object. - */ - void - load_BC_list(const std::vector &list_of_BCs); - - /** - * \brief Assign the boundary condition to the varBC object given some boundary - * condition string vector. - */ - void - assign_boundary_conditions(std::vector &boundary_condition_list, - varBCs &boundary_condition); - - /** - * \brief Retrieve the double from the `model_constants` that are defined from the - * parameters.prm parser. This is essentially just a wrapper for boost::get. - * - * \param constant_name Name of the constant to retrieve. - */ - [[nodiscard]] double - get_model_constant_double(const std::string &constant_name) const - { - Assert(model_constants.find(constant_name) != model_constants.end(), - dealii::ExcMessage( - "PRISMS-PF Error: Mismatch between constants in parameters.prm and " - "customPDE.h. The constant that you attempted to access was " + - constant_name + ".")); - - return boost::get(model_constants.at(constant_name)); - }; - - /** - * \brief Retrieve the int from the `model_constants` that are defined from the - * parameters.prm parser. This is essentially just a wrapper for boost::get. - * - * \param constant_name Name of the constant to retrieve. - */ - [[nodiscard]] int - get_model_constant_int(const std::string &constant_name) const - { - Assert(model_constants.find(constant_name) != model_constants.end(), - dealii::ExcMessage( - "PRISMS-PF Error: Mismatch between constants in parameters.prm and " - "customPDE.h. The constant that you attempted to access was " + - constant_name + ".")); - - return boost::get(model_constants.at(constant_name)); - }; - - /** - * \brief Retrieve the bool from the `model_constants` that are defined from the - * parameters.prm parser. This is essentially just a wrapper for boost::get. - * - * \param constant_name Name of the constant to retrieve. - */ - [[nodiscard]] bool - get_model_constant_bool(const std::string &constant_name) const - { - Assert(model_constants.find(constant_name) != model_constants.end(), - dealii::ExcMessage( - "PRISMS-PF Error: Mismatch between constants in parameters.prm and " - "customPDE.h. The constant that you attempted to access was " + - constant_name + ".")); - - return boost::get(model_constants.at(constant_name)); - }; - - /** - * \brief Retrieve the rank 1 tensor from the `model_constants` that are defined from - * the parameters.prm parser. This is essentially just a wrapper for boost::get. - * - * \param constant_name Name of the constant to retrieve. - */ - [[nodiscard]] dealii::Tensor<1, dim> - get_model_constant_rank_1_tensor(const std::string &constant_name) const - { - Assert(model_constants.find(constant_name) != model_constants.end(), - dealii::ExcMessage( - "PRISMS-PF Error: Mismatch between constants in parameters.prm and " - "customPDE.h. The constant that you attempted to access was " + - constant_name + ".")); - - return boost::get>(model_constants.at(constant_name)); - }; - - /** - * \brief Retrieve the rank 2 tensor from the `model_constants` that are defined from - * the parameters.prm parser. This is essentially just a wrapper for boost::get. - * - * \param constant_name Name of the constant to retrieve. - */ - [[nodiscard]] dealii::Tensor<2, dim> - get_model_constant_rank_2_tensor(const std::string &constant_name) const - { - Assert(model_constants.find(constant_name) != model_constants.end(), - dealii::ExcMessage( - "PRISMS-PF Error: Mismatch between constants in parameters.prm and " - "customPDE.h. The constant that you attempted to access was " + - constant_name + ".")); - - return boost::get>(model_constants.at(constant_name)); - }; - - /** - * \brief Retrieve the elasticity tensor from the `model_constants` that are defined - * from the parameters.prm parser. This is essentially just a wrapper for boost::get. - * - * \param constant_name Name of the constant to retrieve. - */ - [[nodiscard]] dealii::Tensor<2, 2 * dim - 1 + dim / 3> - get_model_constant_elasticity_tensor(const std::string &constant_name) const - { - Assert(model_constants.find(constant_name) != model_constants.end(), - dealii::ExcMessage( - "PRISMS-PF Error: Mismatch between constants in parameters.prm and " - "customPDE.h. The constant that you attempted to access was " + - constant_name + ".")); - - return boost::get>( - model_constants.at(constant_name)); - }; - - // Method to load in the variable attributes - void - loadVariableAttributes(); - - // Nucleation attribute methods - [[nodiscard]] std::vector - get_nucleus_semiaxes(unsigned int var_index) const - { - return nucleation_parameters_list[nucleation_parameters_list_index.at(var_index)] - .semiaxes; - }; - - [[nodiscard]] std::vector - get_nucleus_freeze_semiaxes(unsigned int var_index) const - { - return nucleation_parameters_list[nucleation_parameters_list_index.at(var_index)] - .freeze_semiaxes; - }; - - [[nodiscard]] std::vector - get_nucleus_rotation(unsigned int var_index) const - { - return nucleation_parameters_list[nucleation_parameters_list_index.at(var_index)] - .ellipsoid_rotation; - }; - - [[nodiscard]] double - get_no_nucleation_border_thickness(unsigned int var_index) const - { - return nucleation_parameters_list[nucleation_parameters_list_index.at(var_index)] - .no_nucleation_border_thickness; - }; - - [[nodiscard]] double - get_nucleus_hold_time(unsigned int var_index) const - { - return nucleation_parameters_list[nucleation_parameters_list_index.at(var_index)] - .hold_time; - }; - - dealii::Tensor<2, dim, double> - get_nucleus_rotation_matrix(unsigned int var_index) const - { - return nucleation_parameters_list[nucleation_parameters_list_index.at(var_index)] - .rotation_matrix; - }; - - // Meshing parameters - std::vector domain_size; - std::vector subdivisions; - unsigned int refine_factor; - unsigned int degree; - - // Mesh refinement parameters - bool h_adaptivity; - unsigned int max_refinement_level; - unsigned int min_refinement_level; - unsigned int skip_remeshing_steps; - std::vector refinement_criteria; - - // Output parameters - unsigned int skip_print_steps; - std::string output_file_type; - bool output_vtu_per_process; - std::string output_file_name; - std::vector outputTimeStepList; - bool print_timing_with_output; - - // Time step parameters - double dtValue; - double finalTime; - unsigned int totalIncrements; - - // Elliptic solver parameters - LinearSolverParameters linear_solver_parameters; - - // Nonlinear solver parameters - NonlinearSolverParameters nonlinear_solver_parameters; - - // Pinning point parameters - boost::unordered_map> pinned_point; - - // Variable inputs (I might be able to leave some/all of these in - // variable_attributes) - const AttributesList &var_attributes; - const AttributesList &pp_attributes; - - // Variables needed to calculate the RHS - unsigned int num_var_explicit_RHS, num_var_nonexplicit_RHS; - std::vector varInfoListExplicitRHS, varInfoListNonexplicitRHS; - - // Variables needed to calculate the LHS - unsigned int num_var_LHS; - std::vector varInfoListLHS; - std::vector varChangeInfoListLHS; - - // Variables for loading in initial conditions - std::vector load_ICs; - std::vector load_parallel_file; - std::vector load_file_name; - std::vector load_field_name; - - // Variables for saving/loading checkpoints - bool resume_from_checkpoint; - std::vector checkpointTimeStepList; - - // Postprocessing parameters - unsigned int num_integrated_fields; - bool postProcessingRequired; - std::vector integrated_field_indices; - - // Variable and residual info - std::vector pp_varInfoList; - std::vector pp_baseVarInfoList; - - // List of boundary conditions - std::vector> BC_list; - - // List of user-defined constants - std::map> model_constants; - - // Nucleation parameters - bool nucleation_occurs; - std::vector nucleating_variable_indices; - std::vector nucleation_need_value; - bool evolution_before_nucleation; - // Declare later - // bool multiple_nuclei_per_order_parameter; - double min_distance_between_nuclei; // Only enforced for nuclei placed during - // the same time step - double nucleation_order_parameter_cutoff; - unsigned int steps_between_nucleation_attempts; - double nucleation_start_time; - double nucleation_end_time; - - // Grain remapping parameters - bool grain_remapping_activated; - std::vector variables_for_remapping; // Note: this should be a sorted list - unsigned int skip_grain_reassignment_steps; - double order_parameter_threshold; - double buffer_between_grains; - - bool load_grain_structure; - std::string load_vtk_file_type; // adding this string to know what type of vtk file you - // want to read, it will be passed to - // initialconditions.cc - double min_radius_for_loading_grains; - std::string grain_structure_filename; - std::string grain_structure_variable_name; - unsigned int num_grain_smoothing_cycles; - -private: - /** - * \brief Assign the provided user inputs to parameters for anything related to the - * spatial discretiziation. - */ - void - assign_spatial_discretization_parameters(dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Assign the provided user inputs to parameters for anything related to the - * temporal discretiziation. - */ - void - assign_temporal_discretization_parameters(dealii::ParameterHandler ¶meter_handler); - /** - * \brief Assign the provided user inputs to parameters for anything related to linear - * solves. - */ - void - assign_linear_solve_parameters(dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Assign the provided user inputs to parameters for anything related to - * nonlinear solves. - */ - void - assign_nonlinear_solve_parameters(dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Assign the provided user inputs to parameters for anything related to - * outputs. - */ - void - assign_output_parameters(dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Assign the provided user inputs to parameters for anything related to - * loading in initial condition. - */ - void - assign_load_initial_condition_parameters(dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Assign the provided user inputs to parameters for anything related to - * nucleation. - */ - void - assign_nucleation_parameters(dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Assign the provided user inputs to parameters for anything related to - * grain remapping and grain vtk load-in. - */ - void - assign_grain_parameters(dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Assign the provided user inputs to parameters for anything related to - * boundary conditions. - */ - void - assign_boundary_condition_parameters(dealii::ParameterHandler ¶meter_handler); - - // Method to create the list of time steps where the results should be output - // (called from loadInputParameters) - std::vector - setTimeStepList(const std::string &outputSpacingType, - unsigned int numberOfOutputs, - const std::vector &userGivenTimeStepList); - - void - load_model_constants(inputFileReader &input_file_reader, - dealii::ParameterHandler ¶meter_handler); - - /** - * \brief Compute the number of tensor rows. - */ - unsigned int - compute_tensor_parentheses(const unsigned int n_elements, - const std::vector &tensor_elements); - - /** - * \brief Remove and leading and trailing parentheses. - */ - void - remove_parentheses(std::vector &tensor_elements); - - /** - * \brief Compute a 1st rank tensor from user inputs . - */ - dealii::Tensor<1, dim> - compute_rank_1_tensor_constant(const unsigned int n_elements, - std::vector tensor_elements); - - /** - * \brief Compute a 2nd rank tensor from user inputs . - */ - dealii::Tensor<2, dim> - compute_rank_2_tensor_constant(const unsigned int n_elements, - std::vector tensor_elements); - - /** - * \brief Assign the specified user constant to whatever type. - */ - InputVariant - construct_user_constant(std::vector &model_constants_strings); - - /** - * \brief Assign the primitive user constants (e.g., int, double, bool). - */ - InputVariant - primitive_model_constant(std::vector &model_constants_strings); - - [[nodiscard]] dealii::Tensor<2, 2 * dim - 1 + dim / 3> - get_Cij_tensor(std::vector elastic_constants, - const std::string &elastic_const_symmetry) const; - - dealii::Tensor<2, 2 * dim - 1 + dim / 3> - getCIJMatrix(const elasticityModel model, - const std::vector &constants, - dealii::ConditionalOStream &pcout) const; - - // Private nucleation variables - std::vector> nucleation_parameters_list; - std::map nucleation_parameters_list_index; -}; - -#endif /* INCLUDE_USERINPUTPARAMETERS_H_ */ diff --git a/include/core/user_inputs/boundary_parameters.h b/include/core/user_inputs/boundary_parameters.h new file mode 100644 index 000000000..4662ebc48 --- /dev/null +++ b/include/core/user_inputs/boundary_parameters.h @@ -0,0 +1,248 @@ +#ifndef boundary_parameters_h +#define boundary_parameters_h + +#include + +#include + +#include +#include +#include +#include +#include + +/** + * \brief Class that stores relevant information for boundary conditions of a certain + * field. + */ +template +class boundaryCondition +{ +public: + /** + * \brief Constructor. + */ + boundaryCondition() = default; + + /** + * \brief Destructor. + */ + ~boundaryCondition() = default; + + // Map of boundary conditions and domain boundary for which they correspond to. For a + // simple geometry like a square the boundary ids are marked, in order, by x=0, x=max, + // y=0, y=max. More complex geometries can have somewhat arbitrary ordering, but will + // render some of our assertiosn moot. + std::map boundary_condition_map; + + // A map of boundary values for dirichlet boundary conditions + std::map dirichlet_value_map; +}; + +/** + * \brief Class that holds boundary parameters. + */ +template +class boundaryParameters +{ +public: + /** + * \brief Constructor. + */ + boundaryParameters() = default; + + /** + * \brief Destructor. + */ + ~boundaryParameters() = default; + + /** + * \brief Compute the boundary conditions from the unfiltered string list. + */ + void + compute_boundary_conditions(const AttributesList &var_attributes); + + /** + * \brief Print parameters to summary.log + */ + void + print_parameter_summary() const; + + // Map of unfiltered boundary conditions strings. The first key is the global index. The + // second key is the number of dimensions. + std::map> BC_list; + + // Map of pinned points. The first key is the global index. The pair is the pinned + // value and point. + std::map>> pinned_point_list; + + // Map of boundary conditions. The first key is the global index. The second key is the + // number of dimensions. + std::map>> boundary_condition_list; +}; + +template +inline void +boundaryParameters::compute_boundary_conditions(const AttributesList &var_attributes) +{ + for (const auto &[index, variable] : var_attributes) + { + // Ensure that boundary conditions are specified for all variables and their + // components + if (variable.field_type == fieldType::VECTOR) + { + for (uint i = 0; i < dim; i++) + { + AssertThrow(!BC_list.at(index).at(i).empty(), + dealii::ExcMessage("Boundary conditions must be specified " + "for all components in all vector field.")); + } + } + else + { + AssertThrow(!BC_list.at(index).at(0).empty(), + dealii::ExcMessage("Boundary conditions must be specified " + "for all scalar fields.")); + } + } + + // Helper function that splits boundary condition input (delimiter ",") and assigns + // the correct boundary condition enum. + auto set_boundary = + [&](const std::string &BC_string, const uint &index, const uint &component) + { + // Split string + auto BC_string_list = dealii::Utilities::split_string_list(BC_string); + + // Check that there is either 1 or 2*dim entries in the vector. This can be changed + // later to support other geometries. + AssertThrow( + BC_string_list.size() == 1 || BC_string_list.size() == static_cast(2 * dim), + dealii::ExcMessage("Either 1 or 2*dim boundary conditions must be specified.")); + + // If there is only 1 boundary condition resize BC_string_list, copying the first + // entry. + if (BC_string_list.size() == 1) + { + BC_string_list.resize(static_cast(2 * dim), BC_string_list[0]); + } + + // Assign boundary condition + boundaryCondition condition; + for (uint i = 0; i < (2 * dim); i++) + { + if (boost::iequals(BC_string_list[i], "NATURAL")) + { + condition.boundary_condition_map.emplace(i, boundaryType::NATURAL); + } + else if (boost::iequals(BC_string_list[i].substr(0, 9), "DIRICHLET")) + { + condition.boundary_condition_map.emplace(i, boundaryType::DIRICHLET); + std::string dirichlet_value = + BC_string_list[i].substr(10, BC_string_list[i].size()); + dirichlet_value = dealii::Utilities::trim(dirichlet_value); + condition.dirichlet_value_map.emplace(i, + dealii::Utilities::string_to_double( + dirichlet_value)); + } + else if (boost::iequals(BC_string_list[i], "PERIODIC")) + { + condition.boundary_condition_map.emplace(i, boundaryType::PERIODIC); + } + else if (boost::iequals(BC_string_list[i].substr(0, 7), "NEUMANN")) + { + AssertThrow(false, FeatureNotImplemented("Neumann boundary conditions")); + } + else if (boost::iequals(BC_string_list[i], "NON_UNIFORM_DIRICHLET")) + { + condition.boundary_condition_map.emplace(i, + boundaryType::NON_UNIFORM_DIRICHLET); + } + else if (boost::iequals(BC_string_list[i], "NON_UNIFORM_NEUMANN")) + { + AssertThrow(false, + FeatureNotImplemented("Nonuniform neumann boundary conditions")); + } + else + { + AssertThrow(false, + dealii::ExcMessage("Invalid boundary condition " + + BC_string_list[i])); + } + // If periodic boundary conditions are used, ensure that they are applied on + // both sides of the domain. + if (i % 2 == 0) + { + AssertThrow(boost::iequals(BC_string_list[i], "PERIODIC") == + boost::iequals(BC_string_list[i + 1], "PERIODIC"), + dealii::ExcMessage("Periodic boundary condition must be " + "specified on both sides of domain")); + } + } + + boundary_condition_list[component].emplace(index, condition); + }; + // Process each boundary condition + for (const auto &[index, variable] : var_attributes) + { + if (variable.field_type == fieldType::VECTOR) + { + for (uint i = 0; i < dim; i++) + { + set_boundary(BC_list.at(index).at(i), index, i); + } + } + else + { + set_boundary(BC_list.at(index).at(0), index, 0); + } + } +} + +template +inline void +boundaryParameters::print_parameter_summary() const +{ + conditionalOStreams::pout_summary() + << "================================================\n" + << "\tBoundary Parameters\n" + << "================================================\n"; + + conditionalOStreams::pout_summary() << "Index: "; + for (const auto &[index, component_map] : boundary_condition_list) + { + for (const auto &[component, boundary_condition] : component_map) + { + conditionalOStreams::pout_summary() << index << "\n" + << " Component: " << component << "\n"; + for (const auto &[domain_id, boundary_type] : + boundary_condition.boundary_condition_map) + { + conditionalOStreams::pout_summary() + << " Boundary id: " << domain_id << " " + << "Condition: " << to_string(boundary_type); + if (boundary_type == boundaryType::DIRICHLET) + { + conditionalOStreams::pout_summary() + << " = " << boundary_condition.dirichlet_value_map.at(domain_id); + } + conditionalOStreams::pout_summary() << "\n"; + } + } + } + + if (!pinned_point_list.empty()) + { + conditionalOStreams::pout_summary() << "Pinned field index: "; + } + for (const auto &[index, point_value_map] : pinned_point_list) + { + conditionalOStreams::pout_summary() + << index << "\n" + << " Value: " << point_value_map.first << "\n" + << " Point: " << point_value_map.second << "\n"; + } + conditionalOStreams::pout_summary() << "\n" << std::flush; +} + +#endif \ No newline at end of file diff --git a/include/core/user_inputs/checkpoint_parameters.h b/include/core/user_inputs/checkpoint_parameters.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/core/user_inputs/field_input_parameters.h b/include/core/user_inputs/field_input_parameters.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/core/user_inputs/grain_parameters.h b/include/core/user_inputs/grain_parameters.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/core/inputFileReader.h b/include/core/user_inputs/input_file_reader.h similarity index 92% rename from include/core/inputFileReader.h rename to include/core/user_inputs/input_file_reader.h index e9d4b013f..b5dd23c30 100644 --- a/include/core/inputFileReader.h +++ b/include/core/user_inputs/input_file_reader.h @@ -1,11 +1,9 @@ -// Class to read in the user input from parameters.in - -#ifndef INCLUDE_INPUTFILEREADER_H_ -#define INCLUDE_INPUTFILEREADER_H_ +#ifndef input_file_reader_h +#define input_file_reader_h #include -#include +#include #include #include #include @@ -49,7 +47,7 @@ class inputFileReader /** * \brief Method to check if a line has the desired contents and if so, extract it. */ - static bool + bool parse_line(std::string line, const std::string &keyword, const std::string &entry_name, @@ -59,13 +57,13 @@ class inputFileReader /** * \brief Strip spaces from the front and back of a string. */ - static void + void strip_spaces(std::string &line); /** * \brief Check whether a string starts with a keyword. */ - static bool + bool check_keyword_match(std::string &line, const std::string &keyword); /** @@ -145,7 +143,7 @@ class inputFileReader const AttributesList &pp_attributes; dealii::ParameterHandler parameter_handler; std::set model_constant_names; - unsigned int number_of_dimensions; + uint number_of_dimensions; }; #endif /* INCLUDE_INPUTFILEREADER_H_ */ diff --git a/include/core/user_inputs/linear_solve_parameters.h b/include/core/user_inputs/linear_solve_parameters.h new file mode 100644 index 000000000..69eca74b6 --- /dev/null +++ b/include/core/user_inputs/linear_solve_parameters.h @@ -0,0 +1,112 @@ +#ifndef linear_solve_parameters_h +#define linear_solve_parameters_h + +#include +#include +#include + +/** + * \brief Class that stores relevant linear solve information of a certain field + */ +class linearSolverParameters +{ +public: + /** + * \brief Constructor. + */ + linearSolverParameters() = default; + + /** + * \brief Destructor. + */ + ~linearSolverParameters() = default; + + // Solver tolerance + double tolerance = 1.0e-6; + + // Solver tolerance type + solverToleranceType tolerance_type = solverToleranceType::RELATIVE_RESIDUAL_CHANGE; + + // Max number of iterations for the linear solve + uint max_iterations = 100; + + // Preconditioner + preconditionerType preconditioner = preconditionerType::GMG; + + // Smoothing range for eigenvalues. This denotes the lower bound of eigenvalues that are + // smoothed [1.2 λ^max / smoothing_range, 1.2 λ^max], where λ^max is the estimated + // maximum eigenvalue. A choice between 5 and 20 is usually useful when the + // preconditioner is used as a smoother in multigrid. + double smoothing_range = 15.0; + + // Number of smoother iterations + uint smoother_iterations = 5; + + // Maximum number of CG iterations used to find the maximum eigenvalue + uint eig_cg_n_iterations = 10; +}; + +/** + * \brief Class that holds linear solver parameters. + */ +class linearSolveParameters + +{ +public: + /** + * \brief Constructor. + */ + linearSolveParameters() = default; + + /** + * \brief Destructor. + */ + ~linearSolveParameters() = default; + + /** + * \brief Print parameters to summary.log + */ + void + print_parameter_summary() const; + + // Map of linear solve parameters for fields that require them + std::map linear_solve; +}; + +inline void +linearSolveParameters::print_parameter_summary() const +{ + if (!linear_solve.empty()) + { + conditionalOStreams::pout_summary() + << "================================================\n" + << "\tLinear Solve Parameters\n" + << "================================================\n"; + + for (const auto &[index, linear_solver_parameters] : linear_solve) + { + conditionalOStreams::pout_summary() + << "Index: " << index << "\n" + << " Tolerance: " << linear_solver_parameters.tolerance << "\n" + << " Type: " << to_string(linear_solver_parameters.tolerance_type) << "\n" + << " Max iterations: " << linear_solver_parameters.max_iterations << "\n" + << " Preconditioner: " << to_string(linear_solver_parameters.preconditioner) + << "\n"; + + if (linear_solver_parameters.preconditioner == preconditionerType::GMG) + { + conditionalOStreams::pout_summary() + << " Smoothing range: " << linear_solver_parameters.smoothing_range + << "\n" + << " Smoother iterations: " + << linear_solver_parameters.smoother_iterations << "\n" + << " Max eigenvalue CG iterations: " + << linear_solver_parameters.eig_cg_n_iterations << "\n"; + } + } + + conditionalOStreams::pout_summary() << "\n" << std::flush; + } +} + +#endif \ No newline at end of file diff --git a/include/core/user_inputs/nonlinear_solve_parameters.h b/include/core/user_inputs/nonlinear_solve_parameters.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/core/user_inputs/nucleation_parameters.h b/include/core/user_inputs/nucleation_parameters.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/core/user_inputs/output_parameters.h b/include/core/user_inputs/output_parameters.h new file mode 100644 index 000000000..4875b8b9e --- /dev/null +++ b/include/core/user_inputs/output_parameters.h @@ -0,0 +1,151 @@ +#ifndef output_parameters_h +#define output_parameters_h + +#include +#include +#include + +/** + * \brief Class that holds output parameters. + */ +class outputParameters +{ +public: + /** + * \brief Constructor. + */ + outputParameters() = default; + + /** + * \brief Destructor. + */ + ~outputParameters() = default; + + /** + * \brief Compute the list of increments where the solution is output to file. + */ + void + compute_output_list(const temporalDiscretization &temporal_discretization); + + /** + * \brief Print parameters to summary.log + */ + void + print_parameter_summary() const; + + // Output file type + std::string output_file_type; + + // Output file name + std::string output_file_name; + + // The number of steps between outputting + uint output_frequency = UINT_MAX; + + // Output condition type + std::string output_condition; + + // Number of outputs + uint n_outputs = 0; + + // User given output_list + std::vector user_output_list; + + // Whether to print timing information with output + bool print_timing_with_output = false; + + // List of increments that output the solution to file + std::vector output_list; +}; + +inline void +outputParameters::compute_output_list( + const temporalDiscretization &temporal_discretization) +{ + // If the user has specified a list and we have list output use that and return early + if (output_condition == "LIST") + { + for (const auto &increment : user_output_list) + { + output_list.push_back(static_cast(increment)); + } + return; + } + + // If the number of outputs is 0 return early + if (n_outputs == 0) + { + return; + } + + // If the number of outputs is greater than the number of increments, force them to be + // equivalent + n_outputs = std::min(n_outputs, temporal_discretization.total_increments); + + // Determine the output list from the other criteria + if (output_condition == "EQUAL_SPACING") + { + for (uint iteration = 0; iteration <= temporal_discretization.total_increments; + iteration += temporal_discretization.total_increments / n_outputs) + { + output_list.push_back(iteration); + } + } + else if (output_condition == "LOG_SPACING") + { + output_list.push_back(0); + for (uint output = 1; output <= n_outputs; output++) + { + output_list.push_back(static_cast(std::round( + std::pow(static_cast(temporal_discretization.total_increments), + static_cast(output) / static_cast(n_outputs))))); + } + } + else if (output_condition == "N_PER_DECADE") + { + AssertThrow(temporal_discretization.total_increments > 1, + dealii::ExcMessage("For n per decaded spaced outputs, the number of " + "increments must be greater than 1.")); + + output_list.push_back(0); + output_list.push_back(1); + for (uint iteration = 2; iteration <= temporal_discretization.total_increments; + iteration++) + { + const uint decade = std::ceil(std::log10(iteration)); + const auto step_size = static_cast(std::pow(10, decade) / n_outputs); + if (iteration % step_size == 0) + { + output_list.push_back(iteration); + } + } + } + else + { + AssertThrow(false, dealii::ExcMessage("Invalid output spacing type.")); + } +} + +inline void +outputParameters::print_parameter_summary() const +{ + conditionalOStreams::pout_summary() + << "================================================\n" + << "\tOutput Parameters\n" + << "================================================\n" + << "Output file type: " << output_file_type << "\n" + << "Output file name: " << output_file_name << "\n" + << "Output frequency: " << output_frequency << "\n" + << "Output condition: " << output_condition << "\n" + << "Number of outputs: " << n_outputs << "\n" + << "Print timing info: " << bool_to_string(print_timing_with_output) << "\n"; + + conditionalOStreams::pout_summary() << "Output iteration list: "; + for (const auto &iteration : output_list) + { + conditionalOStreams::pout_summary() << iteration << " "; + } + conditionalOStreams::pout_summary() << "\n\n" << std::flush; +} + +#endif \ No newline at end of file diff --git a/include/core/user_inputs/spatial_discretization.h b/include/core/user_inputs/spatial_discretization.h new file mode 100644 index 000000000..990eec751 --- /dev/null +++ b/include/core/user_inputs/spatial_discretization.h @@ -0,0 +1,110 @@ +#ifndef spatial_discretization_h +#define spatial_discretization_h + +#include +#include +#include + +/** + * \brief Class that holds spatial discretization parameters. + */ +template +class spatialDiscretization +{ +public: + /** + * \brief Constructor. + */ + spatialDiscretization() + : subdivisions(dim, 1) {}; + + /** + * \brief Destructor. + */ + ~spatialDiscretization() = default; + + /** + * \brief Print parameters to summary.log + */ + void + print_parameter_summary() const; + + // Domain extents in each cartesian direction + dealii::Tensor<1, dim, double> domain_size; + + // Mesh subdivisions in each cartesian direction + std::vector subdivisions; + + // Global refinement of mesh + uint refine_factor = 0; + + // Element polynomial degree + uint degree = 1; + + // Whether adaptive meshing (AMR) is enabled + bool has_adaptivity = false; + + // Maximum global refinement for AMR + uint max_refinement_level = 0; + + // Minimum global refinement for AMR + uint min_refinement_level = 0; + + // The number of steps between remeshing + uint remeshing_frequency = UINT_MAX; + + // The criteria used for remeshing + std::vector refinement_criteria; +}; + +template +inline void +spatialDiscretization::print_parameter_summary() const +{ + conditionalOStreams::pout_summary() + << "================================================\n" + << "\tSpatial Discretization\n" + << "================================================\n"; + + if constexpr (dim == 1) + { + conditionalOStreams::pout_summary() << "Domain size: x=" << domain_size[0] << "\n"; + } + else if constexpr (dim == 2) + { + conditionalOStreams::pout_summary() + << "Domain size: x=" << domain_size[0] << ", y=" << domain_size[1] << "\n"; + } + else if constexpr (dim == 3) + { + conditionalOStreams::pout_summary() + << "Domain size: x=" << domain_size[0] << ", y=" << domain_size[1] + << ", z=" << domain_size[2] << "\n"; + } + + conditionalOStreams::pout_summary() + << "Global refinement: " << refine_factor << "\n" + << "Degree: " << degree << "\n" + << "Adaptivity enabled: " << bool_to_string(has_adaptivity) << "\n" + << "Max refinement level: " << max_refinement_level << "\n" + << "Min refinement level: " << min_refinement_level << "\n" + << "Remeshing frequency: " << remeshing_frequency << "\n"; + + if (!refinement_criteria.empty()) + { + conditionalOStreams::pout_summary() << "Refinement criteria:\n"; + } + for (const auto &criterion : refinement_criteria) + { + conditionalOStreams::pout_summary() + << " Variable name: " << criterion.variable_name << "\n" + << " Variable index: " << criterion.variable_index << "\n" + << " Criterion type: " << criterion.criterion_to_string() << "\n" + << " Value lower bound: " << criterion.value_lower_bound << "\n" + << " Value upper bound: " << criterion.value_upper_bound << "\n" + << " Gradient lower bound: " << criterion.gradient_lower_bound << "\n\n"; + } + conditionalOStreams::pout_summary() << "\n" << std::flush; +} + +#endif \ No newline at end of file diff --git a/include/core/user_inputs/temporal_discretization.h b/include/core/user_inputs/temporal_discretization.h new file mode 100644 index 000000000..0e69e463a --- /dev/null +++ b/include/core/user_inputs/temporal_discretization.h @@ -0,0 +1,51 @@ +#ifndef temporal_discretization_h +#define temporal_discretization_h + +#include + +/** + * \brief Class that holds temporal discretization parameters. + */ +class temporalDiscretization +{ +public: + /** + * \brief Constructor. + */ + temporalDiscretization() = default; + + /** + * \brief Destructor. + */ + ~temporalDiscretization() = default; + + /** + * \brief Print parameters to summary.log + */ + void + print_parameter_summary() const; + + // Timestep + double dt = 0.0; + + // Final time + double final_time = 0.0; + + // Total number of increments + uint total_increments = 0; +}; + +inline void +temporalDiscretization::print_parameter_summary() const +{ + conditionalOStreams::pout_summary() + << "================================================\n" + << "\tTemporal Discretization\n" + << "================================================\n" + << "Timestep: " << dt << "\n" + << "Total increments: " << total_increments << "\n" + << "Final time: " << final_time << "\n\n" + << std::flush; +} + +#endif \ No newline at end of file diff --git a/include/core/user_inputs/user_constants.h b/include/core/user_inputs/user_constants.h new file mode 100644 index 000000000..66313d9f1 --- /dev/null +++ b/include/core/user_inputs/user_constants.h @@ -0,0 +1,656 @@ +#ifndef user_constants_h +#define user_constants_h + +#include + +#include +#include + +#include +#include +#include +#include + +/** + * \brief Class the stores and manages user-defined constants. + */ +template +class userConstants +{ +public: + using InputVariant = boost::variant, + dealii::Tensor<2, dim>, + dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)>>; + + /** + * \brief Constructor. + */ + userConstants() = default; + + /** + * \brief Destructor. + */ + ~userConstants() = default; + + /** + * \brief Assign the specified user constant to whatever type. + */ + InputVariant + construct_user_constant(std::vector &model_constants_strings); + + /** + * \brief Retrieve the double from the `model_constants` that are defined from the + * parameters.prm parser. This is essentially just a wrapper for boost::get. + * + * \param constant_name Name of the constant to retrieve. + */ + [[nodiscard]] double + get_model_constant_double(const std::string &constant_name) const; + + /** + * \brief Retrieve the int from the `model_constants` that are defined from the + * parameters.prm parser. This is essentially just a wrapper for boost::get. + * + * \param constant_name Name of the constant to retrieve. + */ + [[nodiscard]] int + get_model_constant_int(const std::string &constant_name) const; + + /** + * \brief Retrieve the bool from the `model_constants` that are defined from the + * parameters.prm parser. This is essentially just a wrapper for boost::get. + * + * \param constant_name Name of the constant to retrieve. + */ + [[nodiscard]] bool + get_model_constant_bool(const std::string &constant_name) const; + + /** + * \brief Retrieve the rank 1 tensor from the `model_constants` that are defined from + * the parameters.prm parser. This is essentially just a wrapper for boost::get. + * + * \param constant_name Name of the constant to retrieve. + */ + [[nodiscard]] dealii::Tensor<1, dim> + get_model_constant_rank_1_tensor(const std::string &constant_name) const; + + /** + * \brief Retrieve the rank 2 tensor from the `model_constants` that are defined from + * the parameters.prm parser. This is essentially just a wrapper for boost::get. + * + * \param constant_name Name of the constant to retrieve. + */ + [[nodiscard]] dealii::Tensor<2, dim> + get_model_constant_rank_2_tensor(const std::string &constant_name) const; + + /** + * \brief Retrieve the elasticity tensor from the `model_constants` that are defined + * from the parameters.prm parser. This is essentially just a wrapper for boost::get. + * + * \param constant_name Name of the constant to retrieve. + */ + [[nodiscard]] dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)> + get_model_constant_elasticity_tensor(const std::string &constant_name) const; + + // List of user-defined constants + std::map model_constants; + +private: + /** + * \brief Compute the number of tensor rows. + */ + uint + compute_tensor_parentheses(const uint n_elements, + const std::vector &tensor_elements); + + /** + * \brief Remove and leading and trailing parentheses. + */ + void + remove_parentheses(std::vector &tensor_elements); + + /** + * \brief Compute a 1st rank tensor from user inputs . + */ + dealii::Tensor<1, dim> + compute_rank_1_tensor_constant(const uint n_elements, + std::vector tensor_elements); + + /** + * \brief Compute a 2nd rank tensor from user inputs . + */ + dealii::Tensor<2, dim> + compute_rank_2_tensor_constant(const uint n_elements, + std::vector tensor_elements); + + /** + * \brief Assign the primitive user constants (e.g., int, double, bool). + */ + InputVariant + primitive_model_constant(std::vector &model_constants_strings); + + [[nodiscard]] dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)> + get_Cij_tensor(std::vector elastic_constants, + const std::string &elastic_const_symmetry) const; + + dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)> + getCIJMatrix(const elasticityModel model, const std::vector &constants) const; +}; + +template +inline double +userConstants::get_model_constant_double(const std::string &constant_name) const +{ + Assert(model_constants.find(constant_name) != model_constants.end(), + dealii::ExcMessage( + "PRISMS-PF Error: Mismatch between constants in parameters.prm and " + "customPDE.h. The constant that you attempted to access was " + + constant_name + ".")); + + return boost::get(model_constants.at(constant_name)); +} + +template +inline int +userConstants::get_model_constant_int(const std::string &constant_name) const +{ + Assert(model_constants.find(constant_name) != model_constants.end(), + dealii::ExcMessage( + "PRISMS-PF Error: Mismatch between constants in parameters.prm and " + "customPDE.h. The constant that you attempted to access was " + + constant_name + ".")); + + return boost::get(model_constants.at(constant_name)); +} + +template +inline bool +userConstants::get_model_constant_bool(const std::string &constant_name) const +{ + Assert(model_constants.find(constant_name) != model_constants.end(), + dealii::ExcMessage( + "PRISMS-PF Error: Mismatch between constants in parameters.prm and " + "customPDE.h. The constant that you attempted to access was " + + constant_name + ".")); + + return boost::get(model_constants.at(constant_name)); +} + +template +inline dealii::Tensor<1, dim> +userConstants::get_model_constant_rank_1_tensor( + const std::string &constant_name) const +{ + Assert(model_constants.find(constant_name) != model_constants.end(), + dealii::ExcMessage( + "PRISMS-PF Error: Mismatch between constants in parameters.prm and " + "customPDE.h. The constant that you attempted to access was " + + constant_name + ".")); + + return boost::get>(model_constants.at(constant_name)); +} + +template +inline dealii::Tensor<2, dim> +userConstants::get_model_constant_rank_2_tensor( + const std::string &constant_name) const +{ + Assert(model_constants.find(constant_name) != model_constants.end(), + dealii::ExcMessage( + "PRISMS-PF Error: Mismatch between constants in parameters.prm and " + "customPDE.h. The constant that you attempted to access was " + + constant_name + ".")); + + return boost::get>(model_constants.at(constant_name)); +} + +template +inline dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)> +userConstants::get_model_constant_elasticity_tensor( + const std::string &constant_name) const +{ + Assert(model_constants.find(constant_name) != model_constants.end(), + dealii::ExcMessage( + "PRISMS-PF Error: Mismatch between constants in parameters.prm and " + "customPDE.h. The constant that you attempted to access was " + + constant_name + ".")); + + return boost::get>( + model_constants.at(constant_name)); +} + +template +inline uint +userConstants::compute_tensor_parentheses( + const uint n_elements, + const std::vector &tensor_elements) +{ + uint open_parentheses = 0; + uint close_parentheses = 0; + + for (uint element = 0; element < n_elements; element++) + { + for (const char c : tensor_elements.at(element)) + { + if (c == '(') + { + ++open_parentheses; + } + else if (c == ')') + { + ++close_parentheses; + } + } + } + + if (open_parentheses != close_parentheses) + { + std::cerr << "PRISMS-PF ERROR: User-defined elastic constant " + "list does not have the same number of open and " + "close parentheses.\n"; + abort(); + } + + return open_parentheses; +} + +template +inline void +userConstants::remove_parentheses(std::vector &tensor_elements) +{ + for (std::string &element : tensor_elements) + { + element.erase(std::remove(element.begin(), element.end(), '('), element.end()); + element.erase(std::remove(element.begin(), element.end(), ')'), element.end()); + } +} + +template +inline dealii::Tensor<1, dim> +userConstants::compute_rank_1_tensor_constant( + const uint n_elements, + std::vector tensor_elements) +{ + AssertThrow(n_elements > 1 && n_elements < 4, + dealii::ExcMessage("PRISMS-PF Error: The columns in user-defined constant " + "tensors cannot be longer than 3 elements (internally " + "truncated to the number of dimensions).")); + + dealii::Tensor<1, dim> temp; + for (uint i = 0; i < dim; i++) + { + temp[i] = dealii::Utilities::string_to_double(tensor_elements.at(i)); + } + + return temp; +} + +template +inline dealii::Tensor<2, dim> +userConstants::compute_rank_2_tensor_constant( + const uint n_elements, + std::vector tensor_elements) +{ + uint row_length = 0; + if (n_elements == 4) + { + AssertThrow(dim < 3, + dealii::ExcMessage( + "PRISMS-PF ERROR: User-defined constant tensor does not have " + "enough elements. For 3D calculations matrices must be 3x3.")); + + row_length = 2; + } + else if (n_elements == 9) + { + row_length = 3; + } + else + { + AssertThrow(false, + dealii::ExcMessage("PRISMS-PF ERROR: User-defined constant tensor does " + "not have the correct number of elements, matrices " + "must be 2x2 or 3x3.")); + } + + dealii::Tensor<2, dim> temp; + for (uint i = 0; i < dim; i++) + { + for (uint j = 0; j < dim; j++) + { + temp[i][j] = + dealii::Utilities::string_to_double(tensor_elements.at((i * row_length) + j)); + } + } + + return temp; +} + +template +inline typename userConstants::InputVariant +userConstants::construct_user_constant( + std::vector &model_constants_strings) +{ + // Ensure that the input includes a value and a type + AssertThrow(model_constants_strings.size() > 1, + dealii::ExcMessage("PRISMS-PF Error: At least two fields are required for " + "user-defined variables (value and type).")); + + std::vector model_constants_type_strings = + dealii::Utilities::split_string_list(model_constants_strings.at( + model_constants_strings.size() - 1), + ' '); + + if (model_constants_strings.size() == 2) + { + return primitive_model_constant(model_constants_strings); + } + else + { + if (boost::iequals(model_constants_type_strings.at(0), "tensor")) + { + const uint n_elements = model_constants_strings.size() - 1; + + const uint open_parentheses = + compute_tensor_parentheses(n_elements, model_constants_strings); + remove_parentheses(model_constants_strings); + + // Rank 1 tensor + if (open_parentheses < 3) + { + return compute_rank_1_tensor_constant(n_elements, model_constants_strings); + } + // Rank 2 tensor + else if (open_parentheses < 5) + { + return compute_rank_2_tensor_constant(n_elements, model_constants_strings); + } + } + else if (boost::iequals(model_constants_type_strings.at(1), "elastic") && + boost::iequals(model_constants_type_strings.at(2), "constants")) + { + const uint n_elements = model_constants_strings.size() - 1; + + remove_parentheses(model_constants_strings); + + // Load in the elastic constants as a vector + std::vector temp_elastic_constants; + for (uint i = 0; i < n_elements; i++) + { + temp_elastic_constants.push_back( + dealii::Utilities::string_to_double(model_constants_strings.at(i))); + } + + const std::string &elastic_const_symmetry = model_constants_type_strings.at(0); + dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)> temp = + get_Cij_tensor(temp_elastic_constants, elastic_const_symmetry); + return temp; + } + else + { + AssertThrow(false, + dealii::ExcMessage( + "PRISMS-PF ERROR: Only user-defined constant tensors may " + "have multiple elements.")); + } + return 0; + } +} + +template +inline typename userConstants::InputVariant +userConstants::primitive_model_constant( + std::vector &model_constants_strings) +{ + std::vector model_constants_type_strings = + dealii::Utilities::split_string_list(model_constants_strings.at( + model_constants_strings.size() - 1), + ' '); + + if (boost::iequals(model_constants_type_strings.at(0), "double")) + { + return dealii::Utilities::string_to_double(model_constants_strings.at(0)); + } + else if (boost::iequals(model_constants_type_strings.at(0), "int")) + { + return dealii::Utilities::string_to_int(model_constants_strings.at(0)); + } + else if (boost::iequals(model_constants_type_strings.at(0), "bool")) + { + bool temp = boost::iequals(model_constants_strings.at(0), "true"); + return temp; + } + else + { + AssertThrow(false, + dealii::ExcMessage( + "PRISMS-PF Error: The type for user-defined variables must be " + "`double`, `int`, `bool`, `tensor`, or `elastic constants`.")); + return 0; + } +} + +template +inline dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)> +userConstants::get_Cij_tensor(std::vector elastic_constants, + const std::string &elastic_const_symmetry) const +{ + // First set the material model + elasticityModel mat_model = ISOTROPIC; + if (elastic_const_symmetry == "isotropic") + { + mat_model = elasticityModel::ISOTROPIC; + } + else if (elastic_const_symmetry == "transverse") + { + mat_model = elasticityModel::TRANSVERSE; + } + else if (elastic_const_symmetry == "orthotropic") + { + mat_model = elasticityModel::ORTHOTROPIC; + } + else if (elastic_const_symmetry == "anisotropic") + { + mat_model = elasticityModel::ANISOTROPIC; + } + else + { + // Should change to an exception + std::cerr << "Elastic material model is invalid, please use isotropic, " + "transverse, orthotropic, or anisotropic\n"; + } + + // If the material model is anisotropic for a 2D calculation but the elastic + // constants are given for a 3D calculation, change the elastic constant + // vector to the 2D form + if ((mat_model == ANISOTROPIC) && (dim == 2) && elastic_constants.size() == 21) + { + std::vector elastic_constants_temp = elastic_constants; + elastic_constants.clear(); + const std::vector indices_2D = {0, 1, 5, 6, 10, 14}; + for (const auto &index : indices_2D) + { + elastic_constants.push_back(elastic_constants_temp.at(index)); + } + } + + return getCIJMatrix(mat_model, elastic_constants); +} + +template +inline dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)> +userConstants::getCIJMatrix(const elasticityModel model, + const std::vector &constants) const +{ + // CIJ.fill(0.0); + dealii::Tensor<2, (2 * dim) - 1 + (dim / 3)> CIJ; + + conditionalOStreams::pout_base << "Reading material model:"; + switch (dim) + { + case 1: + { + conditionalOStreams::pout_base << " 1D "; + // 1D models + switch (model) + { + case ISOTROPIC: + { + conditionalOStreams::pout_base << " ISOTROPIC \n"; + CIJ[0][0] = constants[0]; + break; + } + default: + { + exit(-1); + } + } + break; + } + case 2: + { + conditionalOStreams::pout_base << " 2D "; + // 2D models + switch (model) + { + case ISOTROPIC: + { + conditionalOStreams::pout_base << " ISOTROPIC \n"; + const double E = constants[0]; + const double nu = constants[1]; + const double mu = E / (2 * (1 + nu)); + const double lambda = nu * E / ((1 + nu) * (1 - 2 * nu)); + CIJ[0][0] = lambda + 2 * mu; + CIJ[1][1] = lambda + 2 * mu; + CIJ[2][2] = mu; + CIJ[0][1] = CIJ[1][0] = lambda; + break; + } + case ANISOTROPIC: + { + conditionalOStreams::pout_base << " ANISOTROPIC \n"; + CIJ[0][0] = constants[0]; // C11 + CIJ[1][1] = constants[1]; // C22 + CIJ[2][2] = constants[2]; // C33 + CIJ[0][1] = CIJ[1][0] = constants[3]; // C12 + CIJ[0][2] = CIJ[2][0] = constants[4]; // C13 + CIJ[1][2] = CIJ[2][1] = constants[5]; // C23 + break; + } + default: + { + exit(-1); + } + } + break; + } + case 3: + { + conditionalOStreams::pout_base << " 3D "; + // 3D models + switch (model) + { + case ISOTROPIC: + { + conditionalOStreams::pout_base << " ISOTROPIC \n"; + const double E = constants[0]; + const double nu = constants[1]; + const double mu = E / (2 * (1 + nu)); + const double lambda = nu * E / ((1 + nu) * (1 - 2 * nu)); + CIJ[0][0] = lambda + 2 * mu; + CIJ[1][1] = lambda + 2 * mu; + CIJ[2][2] = lambda + 2 * mu; + CIJ[3][3] = mu; + CIJ[4][4] = mu; + CIJ[5][5] = mu; + CIJ[0][1] = CIJ[1][0] = lambda; + CIJ[0][2] = CIJ[2][0] = lambda; + CIJ[1][2] = CIJ[2][1] = lambda; + break; + } + case TRANSVERSE: + { + conditionalOStreams::pout_base << " TRANSVERSE \n"; + CIJ[0][0] = constants[0]; // C11 + CIJ[1][1] = constants[0]; // C11 + CIJ[2][2] = constants[1]; // C33 + CIJ[3][3] = constants[2]; // C44 + CIJ[4][4] = constants[2]; // C44 + CIJ[5][5] = (constants[0] - constants[3]) / 2.0; //(C11-C12)/2 + CIJ[0][1] = CIJ[1][0] = constants[3]; // C12 + CIJ[0][2] = CIJ[2][0] = constants[4]; // C13 + CIJ[1][2] = CIJ[2][1] = constants[4]; // C13 + break; + } + case ORTHOTROPIC: + { + conditionalOStreams::pout_base << " ORTHOTROPIC \n"; + CIJ[0][0] = constants[0]; // C11 + CIJ[1][1] = constants[1]; // C22 + CIJ[2][2] = constants[2]; // C33 + CIJ[3][3] = constants[3]; // C44 + CIJ[4][4] = constants[4]; // C55 + CIJ[5][5] = constants[5]; // C66 + CIJ[0][1] = CIJ[1][0] = constants[6]; // C12 + CIJ[0][2] = CIJ[2][0] = constants[7]; // C13 + CIJ[1][2] = CIJ[2][1] = constants[8]; // C23 + break; + } + case ANISOTROPIC: + { + conditionalOStreams::pout_base << " ANISOTROPIC \n"; + CIJ[0][0] = constants[0]; // C11 + CIJ[1][1] = constants[1]; // C22 + CIJ[2][2] = constants[2]; // C33 + CIJ[3][3] = constants[3]; // C44 + CIJ[4][4] = constants[4]; // C55 + CIJ[5][5] = constants[5]; // C66 + CIJ[0][1] = CIJ[1][0] = constants[6]; // C12 + CIJ[0][2] = CIJ[2][0] = constants[7]; // C13 + CIJ[0][3] = CIJ[3][0] = constants[8]; // C14 + CIJ[0][4] = CIJ[4][0] = constants[9]; // C15 + CIJ[0][5] = CIJ[5][0] = constants[10]; // C16 + CIJ[1][2] = CIJ[2][1] = constants[11]; // C23 + CIJ[1][3] = CIJ[3][1] = constants[12]; // C24 + CIJ[1][4] = CIJ[4][1] = constants[13]; // C25 + CIJ[1][5] = CIJ[5][1] = constants[14]; // C26 + CIJ[2][3] = CIJ[3][2] = constants[15]; // C34 + CIJ[2][4] = CIJ[4][2] = constants[16]; // C35 + CIJ[2][5] = CIJ[5][2] = constants[17]; // C36 + CIJ[3][4] = CIJ[4][3] = constants[18]; // C45 + CIJ[3][5] = CIJ[5][3] = constants[19]; // C46 + CIJ[4][5] = CIJ[5][4] = constants[20]; // C56 + break; + } + default: + { + exit(-1); + } + } + break; + } + default: + { + exit(-1); + } + } + // print CIJ to terminal + conditionalOStreams::pout_base << "Elasticity matrix (Voigt notation):\n"; + constexpr uint voight_matrix_size = (2 * dim) - 1 + (dim / 3); + for (uint i = 0; i < voight_matrix_size; i++) + { + for (uint j = 0; j < voight_matrix_size; j++) + { + conditionalOStreams::pout_base << std::setw(8) << std::setprecision(3) + << std::scientific << CIJ[i][j] << " "; + } + conditionalOStreams::pout_base << "\n"; + } + conditionalOStreams::pout_base << "\n"; + return CIJ; +} + +#endif \ No newline at end of file diff --git a/include/core/user_inputs/user_input_parameters.h b/include/core/user_inputs/user_input_parameters.h new file mode 100644 index 000000000..eb5e03c20 --- /dev/null +++ b/include/core/user_inputs/user_input_parameters.h @@ -0,0 +1,122 @@ +#ifndef user_input_parameters_h +#define user_input_parameters_h + +#include +#include +#include +#include +#include +#include +#include + +template +class userInputParameters +{ +public: + /** + * \brief Constructor. Reads in user input parameters from file and loads them into + * member variables. + */ + userInputParameters(inputFileReader &input_file_reader, + dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Destructor. + */ + ~userInputParameters() = default; + + // Variable attributes + const AttributesList &var_attributes; + const AttributesList &pp_attributes; + + // Spatial discretization parameters + spatialDiscretization spatial_discretization; + + // Temporal discretization parameters + temporalDiscretization temporal_discretization; + + // Output parameters + outputParameters output_parameters; + + // Boundary parameters + boundaryParameters boundary_parameters; + + // Linear solve paramters + linearSolveParameters linear_solve_parameters; + + // User constants + userConstants user_constants; + +private: + /** + * \brief Assign the provided user inputs to parameters for anything related to the + * spatial discretiziation. + */ + void + assign_spatial_discretization_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user inputs to parameters for anything related to the + * temporal discretiziation. + */ + void + assign_temporal_discretization_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user inputs to parameters for anything related to linear + * solves. + */ + void + assign_linear_solve_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user inputs to parameters for anything related to + * nonlinear solves. + */ + void + assign_nonlinear_solve_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user inputs to parameters for anything related to + * outputs. + */ + void + assign_output_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user inputs to parameters for anything related to + * loading in initial condition. + */ + void + assign_load_initial_condition_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user inputs to parameters for anything related to + * nucleation. + */ + void + assign_nucleation_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user inputs to parameters for anything related to + * grain remapping and grain vtk load-in. + */ + void + assign_grain_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user inputs to parameters for anything related to + * boundaries. + */ + void + assign_boundary_parameters(dealii::ParameterHandler ¶meter_handler); + + /** + * \brief Assign the provided user constants. + */ + void + load_model_constants(inputFileReader &input_file_reader, + dealii::ParameterHandler ¶meter_handler); +}; + +#endif \ No newline at end of file diff --git a/include/core/varTypeEnums.h b/include/core/varTypeEnums.h deleted file mode 100644 index 6bf238cc6..000000000 --- a/include/core/varTypeEnums.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef INCLUDE_VARTYPEENUMS_H_ -#define INCLUDE_VARTYPEENUMS_H_ - -enum fieldType -{ - UNDEFINED_FIELD, - SCALAR, - VECTOR -}; - -enum PDEType -{ - UNDEFINED_PDE, - EXPLICIT_TIME_DEPENDENT, - IMPLICIT_TIME_DEPENDENT, - TIME_INDEPENDENT, - AUXILIARY -}; - -#endif diff --git a/include/core/variableAttributes.h b/include/core/variableAttributes.h deleted file mode 100644 index 5ebd93c38..000000000 --- a/include/core/variableAttributes.h +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef VARIABLEATTRIBUTES_H -#define VARIABLEATTRIBUTES_H - -#include - -#include -#include -#include -#include - -using EvalFlags = dealii::EvaluationFlags::EvaluationFlags; -struct variableAttributes; -using AttributesList = std::map; - -/** - * \brief Structure to hold the variable attributes that will be passed to a - * setInputParameters object. - */ -struct variableAttributes -{ - // Variable attributes - std::string name; - fieldType var_type = UNDEFINED_FIELD; - PDEType eq_type = UNDEFINED_PDE; - bool need_value_nucleation = false; - bool nucleating_variable = false; - bool is_pp = false; - bool is_nonlinear = false; - bool calc_integral = false; - bool output_integral = false; - - // This variable's dependencies - std::set dependencies_value_RHS; - std::set dependencies_gradient_RHS; - std::set dependencies_RHS; - std::set dependencies_value_LHS; - std::set dependencies_gradient_LHS; - std::set dependencies_LHS; - std::set dependencies_value_PP; - std::set dependencies_gradient_PP; - std::set dependencies_PP; - - std::set dependency_set; - - // Evaluation Flags. Tells deal.II whether or not to retrieve the value, grad, hess, - // etc. for equations - EvalFlags eval_flags_explicit_RHS = dealii::EvaluationFlags::nothing; - EvalFlags eval_flags_nonexplicit_RHS = dealii::EvaluationFlags::nothing; - EvalFlags eval_flags_nonexplicit_LHS = dealii::EvaluationFlags::nothing; - - EvalFlags eval_flags_change_nonexplicit_LHS = dealii::EvaluationFlags::nothing; - - EvalFlags eval_flags_residual_explicit_RHS = dealii::EvaluationFlags::nothing; - EvalFlags eval_flags_residual_nonexplicit_RHS = dealii::EvaluationFlags::nothing; - EvalFlags eval_flags_residual_nonexplicit_LHS = dealii::EvaluationFlags::nothing; - - EvalFlags eval_flags_postprocess = dealii::EvaluationFlags::nothing; - EvalFlags eval_flags_residual_postprocess = dealii::EvaluationFlags::nothing; - - /** - * \brief Combine 'value' and 'gradient' residual dependencies to one dependency set per - * RHS, LHS, PP. - */ - void - format_dependencies(); - - /** - * \brief Take user-defined dependency sets to set the evaluation flags for each - * variable. - */ - void - parse_dependencies(std::map &other_var_attributes); - - /** - * \brief Take user-defined dependency sets to set the residual flags for each - * variable. - */ - void - parse_residual_dependencies(); - - /** - * \brief Helper function that returns a set of pointers to the flags that need to be - * set when `other_variable` is dependent on this variable. - * - * \param other_variable Variable that is dependent on this variable. - */ - std::set - eval_flags_for_eq_type(const variableAttributes &other_variable); -}; - -#endif diff --git a/include/core/variableContainer.h b/include/core/variableContainer.h deleted file mode 100644 index f7f311d62..000000000 --- a/include/core/variableContainer.h +++ /dev/null @@ -1,147 +0,0 @@ -// This class permits the access of a subset of indexed fields and gives an -// error if any non-allowed fields are requested -#ifndef VARIBLECONTAINER_H -#define VARIBLECONTAINER_H - -#include -#include -#include -#include - -#include - -#include - -template -class variableContainer -{ -public: -#include - // Constructors - - // Standard contructor, used for most situations - variableContainer(const dealii::MatrixFree &data, - const std::vector &_varInfoList, - const std::vector &_varChangeInfoList); - - variableContainer(const dealii::MatrixFree &data, - const std::vector &_varInfoList); - // Nonstandard constructor, used when only one index of "data" should be used, - // use with care! - variableContainer(const dealii::MatrixFree &data, - const std::vector &_varInfoList, - const unsigned int &fixed_index); - - // Methods to get the value/grad/hess in the residual method (this is how the - // user gets these values in equations.h) - T - get_scalar_value(unsigned int global_variable_index) const; - dealii::Tensor<1, dim, T> - get_scalar_gradient(unsigned int global_variable_index) const; - dealii::Tensor<2, dim, T> - get_scalar_hessian(unsigned int global_variable_index) const; - dealii::Tensor<1, dim, T> - get_vector_value(unsigned int global_variable_index) const; - dealii::Tensor<2, dim, T> - get_vector_gradient(unsigned int global_variable_index) const; - dealii::Tensor<3, dim, T> - get_vector_hessian(unsigned int global_variable_index) const; - - T - get_change_in_scalar_value(unsigned int global_variable_index) const; - dealii::Tensor<1, dim, T> - get_change_in_scalar_gradient(unsigned int global_variable_index) const; - dealii::Tensor<2, dim, T> - get_change_in_scalar_hessian(unsigned int global_variable_index) const; - dealii::Tensor<1, dim, T> - get_change_in_vector_value(unsigned int global_variable_index) const; - dealii::Tensor<2, dim, T> - get_change_in_vector_gradient(unsigned int global_variable_index) const; - dealii::Tensor<3, dim, T> - get_change_in_vector_hessian(unsigned int global_variable_index) const; - - // Methods to set the value residual and the gradient residual (this is how - // the user sets these values in equations.h) - void - set_scalar_value_term_RHS(unsigned int global_variable_index, T val); - void - set_scalar_gradient_term_RHS(unsigned int global_variable_index, - dealii::Tensor<1, dim, T> grad); - void - set_vector_value_term_RHS(unsigned int global_variable_index, - dealii::Tensor<1, dim, T> val); - void - set_vector_gradient_term_RHS(unsigned int global_variable_index, - dealii::Tensor<2, dim, T> grad); - - void - set_scalar_value_term_LHS(unsigned int global_variable_index, T val); - void - set_scalar_gradient_term_LHS(unsigned int global_variable_index, - dealii::Tensor<1, dim, T> grad); - void - set_vector_value_term_LHS(unsigned int global_variable_index, - dealii::Tensor<1, dim, T> val); - void - set_vector_gradient_term_LHS(unsigned int global_variable_index, - dealii::Tensor<2, dim, T> grad); - - // Initialize, read DOFs, and set evaulation flags for each variable - void - reinit_and_eval( - const std::vector *> &src, - unsigned int cell); - void - reinit_and_eval_change_in_solution( - const dealii::LinearAlgebra::distributed::Vector &src, - unsigned int cell, - unsigned int var_being_solved); - - // Only initialize the FEEvaluation object for each variable (used for - // post-processing) - void - reinit(unsigned int cell); - - // Integrate the residuals and distribute from local to global - void - integrate_and_distribute( - std::vector *> &dst); - void - integrate_and_distribute_change_in_solution_LHS( - dealii::LinearAlgebra::distributed::Vector &dst, - const unsigned int var_being_solved); - - // The quadrature point index, a method to get the number of quadrature points - // per cell, and a method to get the xyz coordinates for the quadrature point - unsigned int q_point; - - unsigned int - get_num_q_points() const; - - dealii::Point - get_q_point_location() const; - -private: - // Vectors of the actual FEEvaluation objects for each active variable, split - // into scalar variables and vector variables for type reasons - using scalar_FEEval = dealii::FEEvaluation; - using vector_FEEval = dealii::FEEvaluation; - - boost::unordered_map> scalar_vars_map; - boost::unordered_map> vector_vars_map; - - boost::unordered_map> - scalar_change_in_vars_map; - boost::unordered_map> - vector_change_in_vars_map; - - // Object containing some information about each variable (indices, whether - // the val/grad/hess is needed, etc) - std::vector varInfoList; - std::vector varChangeInfoList; - - // The number of variables - unsigned int num_var; -}; - -#endif diff --git a/include/core/variableValueContainer.h b/include/core/variableValueContainer.h deleted file mode 100644 index f6f6832cd..000000000 --- a/include/core/variableValueContainer.h +++ /dev/null @@ -1,46 +0,0 @@ -// This class permits the access of a subset of indexed fields and gives an -// error if any non-allowed fields are requested -#ifndef VARIBLEVALUECONTAINER_H -#define VARIBLEVALUECONTAINER_H - -class variableValueContainer -{ -public: - variableValueContainer() - : num_entries(0) {}; - - void - set(unsigned int global_variable_index, double variable_value) - { - var_index.push_back(global_variable_index); - value.push_back(variable_value); - num_entries++; - }; - - double - operator()(unsigned int global_variable_index) - { - for (unsigned int i = 0; i < num_entries; i++) - { - if (global_variable_index == var_index[i]) - { - return value[i]; - } - } - - // If this point is reached, the index isn't allowed so an error should be - // thrown - std::cerr << "PRISMS-PF Error: Attempted access of a variable value that was not " - "marked as needed in 'parameters.in'. Double-check the indices in " - "user functions where a variable value is requested." - << std::endl; - abort(); - }; - -private: - std::vector var_index; - std::vector value; - unsigned int num_entries; -}; - -#endif diff --git a/include/core/variableAttributeLoader.h b/include/core/variable_attribute_loader.h similarity index 64% rename from include/core/variableAttributeLoader.h rename to include/core/variable_attribute_loader.h index 926ca033d..db800645c 100644 --- a/include/core/variableAttributeLoader.h +++ b/include/core/variable_attribute_loader.h @@ -1,19 +1,19 @@ -#ifndef VARIABLEATTRIBUTELOADER_H -#define VARIABLEATTRIBUTELOADER_H +#ifndef variable_attribute_loader_h +#define variable_attribute_loader_h -#include -#include +#include +#include #include #include -using EvalFlags = dealii::EvaluationFlags::EvaluationFlags; - /** * \brief Class to manage the variable attributes that the user specifies. */ class variableAttributeLoader { public: + using EvalFlags = dealii::EvaluationFlags::EvaluationFlags; + /** * \brief Constructor. */ @@ -64,28 +64,28 @@ class variableAttributeLoader * \param name Name of variable at `index` */ void - set_variable_name(const unsigned int &index, const std::string &name) const; + set_variable_name(const uint &index, const std::string &name) const; /** - * \brief Set the field type of the variable at `index` to `var_type` where `var_type` - * can be `SCALAR` or `VECTOR`. + * \brief Set the field type of the variable at `index` to `field_type` where + * `field_type` can be `SCALAR` or `VECTOR`. * * \param index Index of variable - * \param var_type Field type of variable at `index` (`SCALAR` or `VECTOR`). + * \param field_type Field type of variable at `index` (`SCALAR` or `VECTOR`). */ void - set_variable_type(const unsigned int &index, const fieldType &var_type) const; + set_variable_type(const uint &index, const fieldType &field_type) const; /** - * \brief Set the PDE type of the variable at `index` to `var_eq_type` where - *`var_eq_type`can be `EXPLICIT_TIME_DEPENDENT`, `IMPLICIT_TIME_DEPENDENT`, + * \brief Set the PDE type of the variable at `index` to `pde_type` where + *`pde_type`can be `EXPLICIT_TIME_DEPENDENT`, `IMPLICIT_TIME_DEPENDENT`, *`TIME_INDEPENDENT`, `AUXILIARY`. * * \param index Index of variable - * \param var_eq_type PDE type of variable at `index`. + * \param pde_type PDE type of variable at `index`. */ void - set_variable_equation_type(const unsigned int &index, const PDEType &var_eq_type) const; + set_variable_equation_type(const uint &index, const PDEType &pde_type) const; /** * \brief Add dependencies for the value term of the RHS equation of the variable at @@ -96,8 +96,7 @@ class variableAttributeLoader * variable at `index` Hint: "variable, grad(variable), hess(variable)" */ void - set_dependencies_value_term_RHS(const unsigned int &index, - const std::string &dependencies); + set_dependencies_value_term_RHS(const uint &index, const std::string &dependencies); /** * \brief Add dependencies for the gradient term of the RHS equation of the variable @@ -108,8 +107,7 @@ class variableAttributeLoader * variable at `index` Hint: "variable, grad(variable), hess(variable)" */ void - set_dependencies_gradient_term_RHS(const unsigned int &index, - const std::string &dependencies); + set_dependencies_gradient_term_RHS(const uint &index, const std::string &dependencies); /** * \brief Add dependencies for the value term of the LHS equation of the variable at @@ -120,8 +118,8 @@ class variableAttributeLoader * variable at `index` Hint: "variable, grad(variable), hess(variable)" */ void - set_dependencies_value_term_LHS(const unsigned int &index, - const std::string &dependencies) const; + set_dependencies_value_term_LHS(const uint &index, + const std::string &dependencies) const; /** * \brief Add dependencies for the gradient term of the LHS equation of the variable @@ -132,8 +130,8 @@ class variableAttributeLoader * variable at `index` Hint: "variable, grad(variable), hess(variable)" */ void - set_dependencies_gradient_term_LHS(const unsigned int &index, - const std::string &dependencies) const; + set_dependencies_gradient_term_LHS(const uint &index, + const std::string &dependencies) const; /** * \brief Insert dependencies for the value term of the RHS equation of the variable at @@ -145,8 +143,7 @@ class variableAttributeLoader */ template void - insert_dependencies_value_term_RHS(const unsigned int &index, - const Iterable &dependencies); + insert_dependencies_value_term_RHS(const uint &index, const Iterable &dependencies); /** * \brief Insert dependencies for the gradient term of the RHS equation of the variable @@ -158,8 +155,7 @@ class variableAttributeLoader */ template void - insert_dependencies_gradient_term_RHS(const unsigned int &index, - const Iterable &dependencies); + insert_dependencies_gradient_term_RHS(const uint &index, const Iterable &dependencies); /** * \brief Insert dependencies for the value term of the LHS equation of the variable at @@ -171,8 +167,8 @@ class variableAttributeLoader */ template void - insert_dependencies_value_term_LHS(const unsigned int &index, - const Iterable &dependencies) const; + insert_dependencies_value_term_LHS(const uint &index, + const Iterable &dependencies) const; /** * \brief Insert dependencies for the gradient term of the LHS equation of the variable @@ -184,38 +180,8 @@ class variableAttributeLoader */ template void - insert_dependencies_gradient_term_LHS(const unsigned int &index, - const Iterable &dependencies) const; - - /** - * \brief Flag whether the variable at `index` is needed to calculate the nucleation - * probability. - * - * \param index Index of variable - * \param flag true: variable is needed, false: variable is not needed. - */ - void - set_need_value_nucleation(const unsigned int &index, const bool &flag) const; - - /** - * \brief Flag whether the variable at `index` is can have a nucleation event. - * - * \param index Index of variable - * \param flag true: variable can nucleate, false: variable can not nucleate. - */ - void - set_allowed_to_nucleate(const unsigned int &index, const bool &flag) const; - - /** - * \brief (Postprocess only) Flag whether the postprocessing variable at `index` should - * have its domain integral calculated and output. - * - * \param index Index of variable - * \param flag true: calculate and output the integral of the field over the domain, - * false: do nothing - */ - void - set_output_integral(const unsigned int &index, const bool &flag) const; + insert_dependencies_gradient_term_LHS(const uint &index, + const Iterable &dependencies) const; private: /** @@ -245,45 +211,36 @@ class variableAttributeLoader * \brief Validate that the variable name is not empty and does not contain any * forbidden substrings (names). */ - static void + void validate_variable_name(const std::string &name, const std::set &forbidden_names, const std::string &context, - unsigned int index); + uint index); /** * \brief Populate dependencies that we should expect from the user. */ - static void + void populate_dependencies( const std::set> ®_delimiters, + const std::set> &dep_type_delimiters, const std::string &variable_name, - unsigned int index, + uint index, std::set ®_possible_deps, std::map> &change_possible_deps); /** * \brief Validate the dependencies (RHS or LHS) that the user has provided. */ - static void + void validate_dependencies( const std::set &dependencies, const std::string &context, - unsigned int index, + uint index, const std::string &variable_name, const std::set ®_possible_deps, const std::map> &change_possible_deps); - /** - * \brief Validate the postprocess variables. - */ - static void - validate_postprocess_variable(const std::string &name, - const std::set &name_list, - const std::set ®_possible_deps, - const variableAttributes &pp_variable, - unsigned int index); - /** * \brief Utility to remove whitespace from strings */ diff --git a/include/core/variable_attributes.h b/include/core/variable_attributes.h new file mode 100644 index 000000000..b9181ec71 --- /dev/null +++ b/include/core/variable_attributes.h @@ -0,0 +1,194 @@ +#ifndef variable_attributes_h +#define variable_attributes_h + +#include + +#include +#include +#include +#include +#include + +using EvalFlags = dealii::EvaluationFlags::EvaluationFlags; +struct variableAttributes; +using AttributesList = std::map; + +/** + * \brief Simple hash function for pairs. + */ +struct pairHash +{ +public: + template + std::size_t + operator()(const std::pair &x) const + { + return std::hash()(x.first) ^ std::hash()(x.second); + } +}; + +/** + * \brief Structure to hold the variable attributes of a field. This includes things like + * the name, equation type, whether it's nonlinear, and its dependence on other variables. + */ +struct variableAttributes +{ + /** + * \brief Field name. + */ + std::string name; + + /** + * \brief Field index. + */ + uint field_index; + + /** + * \brief Field type (SCALAR/VECTOR). + */ + fieldType field_type = fieldType::UNDEFINED_FIELD; + + /** + * \brief PDE type (EXPLICIT/NONEXPLICIT). + */ + PDEType pde_type = PDEType::UNDEFINED_PDE; + + /** + * \brief Internal classification for the field solve type. + */ + fieldSolveType field_solve_type = fieldSolveType::UNDEFINED_SOLVE; + + /** + * \brief The user-inputted dependencies for the RHS value term. + */ + std::set dependencies_value_RHS; + + /** + * \brief The user-inputted dependencies for the RHS gradient term. + */ + std::set dependencies_gradient_RHS; + + /** + * \brief The collection of value and gradient dependencies for the RHS. + */ + std::set dependencies_RHS; + + /** + * \brief The user-inputted dependencies for the LHS value term. + */ + std::set dependencies_value_LHS; + + /** + * \brief The user-inputted dependencies for the LHS gradient term. + */ + std::set dependencies_gradient_LHS; + + /** + * \brief The collection of value and gradient dependencies for the LHS. + */ + std::set dependencies_LHS; + + /** + * \brief A map of evaluation flags for the dependencies of the current variable's RHS. + * This will tell deal.II whether to evaluate the value, gradient, and/or hessian for + * the specified field. + */ + std::unordered_map, EvalFlags, pairHash> + eval_flag_set_RHS; + + /** + * \brief A map of evaluation flags for the dependencies of the current variable's LHS. + * This will tell deal.II whether to evaluate the value, gradient, and/or hessian for + * the specified field. + */ + std::unordered_map, EvalFlags, pairHash> + eval_flag_set_LHS; + + /** + * \brief Evaluation flags for the types of residual the user is expected to submit to + * on the RHS. + */ + EvalFlags eval_flags_residual_RHS = dealii::EvaluationFlags::nothing; + + /** + * \brief Evaluation flags for the types of residual the user is expected to submit to + * on the LHS. This is empty for EXPLICIT fields. + */ + EvalFlags eval_flags_residual_LHS = dealii::EvaluationFlags::nothing; + + /** + * \brief A simplified set of evaluation flags for the dependencies of the current + * variable's LHS & RHS. This will help determine the fieldSolveType of the field. + * Fields indices where the evaluation flags are not 0 (not nothing) are included. + * Additionally, explicit fields are excluded to speed up the graph search. + */ + std::set dependency_set; + + /** + * \brief Combine 'value' and 'gradient' residual dependencies to one dependency set per + * RHS and LHS. This will populate `dependencies_RHS` and `dependencies_LHS`. + */ + void + format_dependencies(); + + /** + * \brief Take user-defined dependency sets to set the residual flags for each + * variable. + */ + void + parse_residual_dependencies(); + + /** + * \brief Take user-defined dependency sets to set the evaluation flags for each + * variable. + */ + void + parse_dependencies(AttributesList &other_var_attributes); + + /** + * \brief Using the assigned evaluation flags determine the solve type for this + * equation. + */ + void + determine_field_solve_type(AttributesList &other_var_attributes, + const bool &is_postprocessed = false); + + /** + * \brief Print variable attributes to summary.log + */ + void + print() const; + +private: + /** + * \brief Validate a dependency. + */ + void + validate_dependency(const std::string &variation, + dependencyType dep_type, + const uint &other_index, + const std::string &context) const; + + /** + * \brief Compute the dependency set from eval_flag_set_RHS & eval_flag_set_LHS + */ + void + compute_dependency_set(const AttributesList &other_var_attributes); + + /** + * \brief Find the circular dependencies based on simple DFS algorithm. + */ + void + find_circular_dependencies(const AttributesList &other_var_attributes); + + /** + * \brief Recursive DFS + */ + void + recursive_DFS(const AttributesList &other_var_attributes, + std::set &visited, + std::set ¤t_stack, + const uint &vertex); +}; + +#endif diff --git a/include/core/variable_container.h b/include/core/variable_container.h new file mode 100644 index 000000000..57caf1fe9 --- /dev/null +++ b/include/core/variable_container.h @@ -0,0 +1,296 @@ +#ifndef variable_container_h +#define variable_container_h + +#include +#include +#include +#include + +#include +#include +#include + +/** + * \brief This class permits the access of a subset of indexed fields and gives an error + * if any non-allowed fields are requested. + * + * \tparam dim The number of dimensions in the problem. + * \tparam degree The polynomial degree of the shape functions. + * \tparam number Datatype to use for `dealii::VectorizedArray`. Either + * double or float. + */ +template +class variableContainer +{ +public: + using EvalFlags = dealii::EvaluationFlags::EvaluationFlags; + using scalarValue = dealii::VectorizedArray; + using scalarGrad = dealii::Tensor<1, dim, dealii::VectorizedArray>; + using scalarHess = dealii::Tensor<2, dim, dealii::VectorizedArray>; + using vectorValue = dealii::Tensor<1, dim, dealii::VectorizedArray>; + using vectorGrad = dealii::Tensor<2, dim, dealii::VectorizedArray>; + using vectorHess = dealii::Tensor<3, dim, dealii::VectorizedArray>; + + /** + * \brief Constructor. + */ + variableContainer(const dealii::MatrixFree &data, + const AttributesList &_subset_attributes, + const solveType &_solve_type); + + /** + * \brief Return the value of the specified scalar field. + */ + scalarValue + get_scalar_value(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the gradient of the specified scalar field. + */ + scalarGrad + get_scalar_gradient(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the hessian of the specified scalar field. + */ + scalarHess + get_scalar_hessian(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the diagonal of the hessian of the specified scalar field. + */ + scalarGrad + get_scalar_hessian_diagonal( + uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the laplacian of the specified scalar field. + */ + scalarValue + get_scalar_laplacian(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the value of the specified vector field. + */ + vectorValue + get_vector_value(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the gradient of the specified vector field. + */ + vectorGrad + get_vector_gradient(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the hessian of the specified vector field. + */ + vectorHess + get_vector_hessian(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the diagonal of the hessian of the specified vector field. + */ + vectorGrad + get_vector_hessian_diagonal( + uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the laplacian of the specified vector field. + */ + vectorValue + get_vector_laplacian(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the divergence of the specified vector field. + */ + scalarValue + get_vector_divergence(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the symmetric gradient of the specified vector field. + */ + vectorGrad + get_vector_symmetric_gradient( + uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Return the curl of the specified vector field. Note that this is scalarValue + * type for 2D and vectorValue type for 3D. + */ + dealii::Tensor<1, (dim == 2 ? 1 : dim), dealii::VectorizedArray> + get_vector_curl(uint global_variable_index, + dependencyType dependency_type = dependencyType::NORMAL) const; + + /** + * \brief Set the residual value of the specified scalar field. + */ + void + set_scalar_value_term(uint global_variable_index, + scalarValue val, + dependencyType dependency_type = dependencyType::NORMAL); + + /** + * \brief Set the residual gradient of the specified scalar field. + */ + void + set_scalar_gradient_term(uint global_variable_index, + scalarGrad grad, + dependencyType dependency_type = dependencyType::NORMAL); + + /** + * \brief Set the residual value of the specified vector field. + */ + void + set_vector_value_term(uint global_variable_index, + vectorValue val, + dependencyType dependency_type = dependencyType::NORMAL); + + /** + * \brief Set the residual gradient of the specified vector field. + */ + void + set_vector_gradient_term(uint global_variable_index, + vectorGrad grad, + dependencyType dependency_type = dependencyType::NORMAL); + + /** + * \brief TODO: Add comments + */ + void + eval_local_operator( + const std::function> &)> + &func, + dealii::LinearAlgebra::distributed::Vector &dst, + const LinearAlgebra::distributed::Vector &src, + const std::pair &cell_range); + + /** + * \brief TODO: Add comments + */ + void + eval_local_diagonal( + const std::function> &)> + &func, + dealii::LinearAlgebra::distributed::Vector &dst, + const std::pair &cell_range, + const uint &global_var_index); + +private: + using scalar_FEEval = dealii::FEEvaluation; + using vector_FEEval = dealii::FEEvaluation; + + /** + * \brief Check that a variable value/gradient/hessians was marked as needed and thus + * properly initialized. + */ + void + access_valid(const uint &global_variable_index, + const dependencyType &dependency_type, + const EvalFlags &flag) const; + + /** + * \brief Return the number of quadrature points. + */ + [[nodiscard]] uint + get_n_q_points() const; + + /** + * \brief Return the quadrate point location. + */ + [[nodiscard]] dealii::Point> + get_q_point_location() const; + + /** + * \brief Initialize, read DOFs, and set evaulation flags for each variable. + */ + void + reinit_and_eval(const dealii::LinearAlgebra::distributed::Vector &src, + uint cell); + + /** + * \brief Initialize the cell for all dependencies of a certain variable index. + */ + void + reinit(uint cell, const uint &global_variable_index); + + /** + * \brief Evaluate the flags on the cell for all dependencies of a certain variable + * index. + */ + void + eval(const uint &global_variable_index); + + /** + * \brief Integrate the residuals and distribute from local to global. + */ + void + integrate_and_distribute(dealii::LinearAlgebra::distributed::Vector &dst); + + /** + * \brief Integrate the residuals for a certain variable index. + */ + void + integrate(const uint &global_variable_index); + + /** + * \brief Map of FEEvaluation objects for each active scalar variables. The first + * mapping is for the global variable and the second is for the dependencyType. + */ + std::unordered_map>> + scalar_vars_map; + + /** + * \brief Map of FEEvaluation objects for each active vector variables. The first + * mapping is for the global variable and the second is for the dependencyType. + */ + std::unordered_map>> + vector_vars_map; + + /** + * \brief The attribute list of the relevant subset of variables. + */ + const AttributesList &subset_attributes; + + /** + * \brief The solve type + */ + const solveType solve_type; + + /** + * \brief The number of variables. + */ + uint n_variables; + + /** + * \brief The quadrature point index. + */ + uint q_point = 0; + + /** + * \brief Number of DoFs per cell. + */ + uint n_dofs_per_cell; + + /** + * \brief Diagonal matrix that is used for preconditioning. + */ + std::unique_ptr>> diagonal; +}; + +#endif diff --git a/include/field_input/IntegrationTools/LICENSE b/include/field_input/IntegrationTools/LICENSE deleted file mode 100644 index 96efa482a..000000000 --- a/include/field_input/IntegrationTools/LICENSE +++ /dev/null @@ -1,475 +0,0 @@ -This directory contains the IntegrationTools code developed by the PRISMS Center -at the University of Michigan, Ann Arbor, USA. - -(c) 2014 The Regents of the University of Michigan -PRISMS Center http://prisms.engin.umich.edu - -This code is a free software; you can use it, redistribute it, -and/or modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either version -2.1 of the License, or (at your option) any later version. - -The full text of the GNU Lesser General Public version 2.1 is quoted -below. - - ----------------------------------------------------------------------- - - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS diff --git a/include/field_input/IntegrationTools/PField.hh b/include/field_input/IntegrationTools/PField.hh deleted file mode 100644 index 134878c72..000000000 --- a/include/field_input/IntegrationTools/PField.hh +++ /dev/null @@ -1,2 +0,0 @@ -#include -#include \ No newline at end of file diff --git a/include/field_input/IntegrationTools/datastruc/Bin.hh b/include/field_input/IntegrationTools/datastruc/Bin.hh deleted file mode 100644 index 21d876fe8..000000000 --- a/include/field_input/IntegrationTools/datastruc/Bin.hh +++ /dev/null @@ -1,196 +0,0 @@ -#ifndef Bin_HH -#define Bin_HH - -#include -#include -#include - -/** - * \brief A class for binning things. - * - * \tparam T typename thats being binned. - * \tparam Coordinate class. - */ -template -class Bin -{ -private: - PNDArray> _item; - - std::vector _min; // min coord - std::vector _incr; // histogram spacing along each direction (uniform along - // each dimension) - std::vector _N; // number of bins along each direction - std::vector _max; // max coord - std::vector _indices; - -public: - Bin() = default; - - /// Construct a Bin - /// 'min': minimum value of each coordinate component - /// 'incr': bin spacing along each direction (this is uniform along each dimension) - /// 'N': number of bins along each direction - /// - /// For example, to bin the range (0->10, 0->20, 10->100), with size 1 bin spacing: - /// Bin( {0,0,10}, {1,1,1}, {10, 20, 90}) - /// - Bin(const std::vector &min, std::vector &incr, std::vector &N) - { - _min = min; - _incr = incr; - _N = N; - _max = std::vector(_min.size()); - for (unsigned int i = 0; i < _min.size(); i++) - _max[i] = _min[i] + _incr[i] * _N[i]; - _indices = _N; - _item.resize(_N); - } - - void - clear() - { - (*this) = Bin(); - } - - std::vector & - min() - { - return _min; - } - - std::vector & - max() - { - return _max; - } - - /// Add a new item to the bin containing a given coordinate - void - add(const T &newitem, const Coordinate &coord) - { - indices(coord, _indices); - _item(_indices).push_back(newitem); - } - - /// Add a new item to the bin containing a given coordinate, - /// if an equivalent item is not already present - /// return 'true' if added succesfully, 'false' if not - void - add_once(const T &newitem, const Coordinate &coord) - { - indices(coord, _indices); - std::vector &singlebin = _item(_indices); - for (int i = 0; i < singlebin.size(); i++) - { - if (singlebin[i] == newitem) - { - return; - } - } - - singlebin.push_back(newitem); - } - - /// Add a new item to all bins that fall in cuboid defined by 'min' and 'max' - /// Coordinates - template - void - add_range(const T &newitem, const PCoord &min, const PCoord &max) - { - int i; - std::vector index_min(_item.order()); - std::vector index_max(_item.order()); - indices(min, index_min); - indices(max, index_max); - - _indices = index_min; - - while (true) - { - _item(_indices).push_back(newitem); - - i = 0; - while (_indices[i] == index_max[i]) - { - i++; - if (i == _item.order()) - { - return; - } - } - - _indices[i]++; - - for (i = i - 1; i >= 0; i--) - { - _indices[i] = index_min[i]; - } - } - } - - std::vector & - contents(const Coordinate &coord) - { - indices(coord, _indices); - return _item(_indices); - } - - // maximum size of any bin - int - max_size() - { - unsigned int max = 0; - for (int i = 0; i < _item.volume(); i++) - { - if (_item(i).size() > max) - { - max = _item(i).size(); - } - } - return max; - } - -private: - /// Set 'term' to be the indices into '_item' PNDArray of the bin that contains - /// 'coord' - /// Return 'false' if unsuccesful, 'true' if succesful - template - void - indices(const PCoord &coord, std::vector &term) - { - for (int i = 0; i < _item.order(); i++) - { - if ((coord[i] < _min[i]) || (coord[i] > _max[i])) - throw std::domain_error("Invalid coord, out of bin range"); - term[i] = std::floor((coord[i] - _min[i]) / _incr[i]); - } - } - - /// Set 'term' to be the indices into '_item' PNDArray of the bin that contains - /// 'coord' - /// Return 'false' if unsuccesful, 'true' if succesful - template - void - max_indices(const PCoord &coord, std::vector &term) - { - for (int i = 0; i < _item.order(); i++) - { - if ((coord[i] < _min[i]) || (coord[i] > _max[i])) - throw std::domain_error("Invalid coord, out of bin range"); - - if (std::floor((coord[i] - _min[i]) / _incr[i]) == - (coord[i] - _min[i]) / _incr[i]) - { - term[i] = std::floor((coord[i] - _min[i]) / _incr[i]); - term[i]--; - } - else - { - term[i] = std::floor((coord[i] - _min[i]) / _incr[i]); - } - } - } -}; - -#endif diff --git a/include/field_input/IntegrationTools/datastruc/PNDArray.hh b/include/field_input/IntegrationTools/datastruc/PNDArray.hh deleted file mode 100644 index a5fe2fce2..000000000 --- a/include/field_input/IntegrationTools/datastruc/PNDArray.hh +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef PNDArray_HH -#define PNDArray_HH - -#include -#include -#include - -/** - * \brief A Tensor class, takes int valued IndexContainer and returns OutType value. - */ -template -class PNDArray -{ -private: - std::vector _dim; // dimension along each compenent - std::vector _unroll; // used to translate from tensor indices to linear index - std::vector _val; // unrolled list of coefficients (first index is outer - // loop) - int _order; // _dim.size() - int _volume; // _coeff_tensor.size() = product_i(_dim[i]) - -public: - PNDArray() - : _order(0) - , _volume(0) - {} - - PNDArray(const std::vector &dim) - { - resize(dim); - } - - [[nodiscard]] PNDArray(const std::vector &dim, const std::vector &value) - { - resize(dim); - if (_volume != value.size()) - { - std::cerr << "Error in PNDArray(const std::vector &dim, const " - "std::vector &value)." - << std::endl; - std::cerr << " value.size() does not match volume based on dim." << std::endl; - exit(1); - } - _val = value; - } - - [[nodiscard]] int - order() const - { - return _order; - } - - [[nodiscard]] int - volume() const - { - return _volume; - } - - void - resize(const std::vector &dim) - { - _dim = dim; - _order = _dim.size(); - _volume = calc_volume(_dim); - _val.resize(_volume); - generate_unroll(); - } - - void - reshape(const std::vector &dim) - { - if (calc_volume(dim) == _volume) - { - _dim = dim; - _order = _dim.size(); - generate_unroll(); - } - else - { - std::cerr << "Error in PNDArray::reshape. Volume is not equivalent." << std::endl; - exit(1); - } - } - - void - clear() - { - _val.clear(); - _dim.clear(); - _unroll.clear(); - _order = 0; - _volume = 0; - } - - [[nodiscard]] const std::vector & - dim() const - { - return _dim; - } - - [[nodiscard]] int - dim(int i) const - { - return _dim[i]; - } - - OutType & - operator()(int i) - { - return _val[i]; - } - - template - OutType & - operator()(const IndexContainer &term) - { - return _val[linear_index(term)]; - } - - template - int - linear_index(const IndexContainer &term) const - { - int lindex = 0; - for (unsigned int i = 0; i < _unroll.size(); i++) - { - lindex += term[i] * _unroll[i]; - } - return lindex; - } - - template - void - tensor_indices(int lindex, IndexContainer &term) const - { // assumes term.size() == order() (the tensor order) - // not sure if this is how we want to do it, but it avoids assuming push_back() or - // resize() - - for (int i = 0; i < _unroll.size(); i++) - { - term[i] = lindex / _unroll[i]; - lindex -= term[i] * _unroll[i]; - } - } - -private: - int - calc_volume(const std::vector &dim) - { - if (dim.empty()) - { - return 0; - } - - int vol = 1; - for (int i : dim) - { - vol *= i; - } - return vol; - } - - void - generate_unroll() - { - _unroll.resize(_dim.size()); - _unroll[_dim.size() - 1] = 1; - for (int i = _dim.size() - 2; i >= 0; i--) - { - _unroll[i] = _unroll[i + 1] * _dim[i + 1]; - } - } -}; - -#endif diff --git a/include/field_input/IntegrationTools/pfield/Body.hh b/include/field_input/IntegrationTools/pfield/Body.hh deleted file mode 100644 index e6c13ea1f..000000000 --- a/include/field_input/IntegrationTools/pfield/Body.hh +++ /dev/null @@ -1,294 +0,0 @@ -#ifndef Body_HH -#define Body_HH - -#include -#include -#include -#include -#include -#include - -namespace PRISMS -{ - - /** - * \brief A class for a Body-a combination of Mesh and Field(s)-used in file read-in. - */ - template - class Body - { - public: - Mesh mesh; - - std::vector> scalar_field; - - /** - * \brief Constructor. - */ - Body() = default; - - /** - * \brief Read from a 2D/3D vtk file with quad/hex elements - */ - void - read_vtk(const std::string &vtkfile) - { - // Read vtk file - std::ifstream infile(vtkfile.c_str()); - if (!infile.is_open()) - { - throw std::runtime_error("Could not open VTK file: " + vtkfile); - } - - // Read mesh info - mesh.read_vtk(infile); - - // Read point data - std::istringstream ss; - std::string str; - std::string name; - std::string type; - std::string line; - int numcomp = 0; - unsigned long int Npoints = 0; - - while (std::getline(infile, line)) - { - // Check for Point data - if (line.rfind("POINT_DATA", 0) == 0) - { - ss.clear(); - ss.str(line); - ss >> str >> Npoints; - } - - // Check for Scalar data - if (line.rfind("SCALARS", 0) == 0) - { - ss.clear(); - ss.str(line); - ss >> str >> name >> type >> numcomp; - - // Skip LOOKUP_TABLE line that follows SCALAR line - std::getline(infile, line); - - // Read data - std::vector gid(Npoints); - for (auto &value : gid) - { - infile >> value; - } - - // Check dim - switch (DIM) - { - case 1: - throw std::runtime_error( - "1D dimensional vtk read-in is not currently supported."); - break; - case 2: - break; - case 3: - break; - default: - throw std::runtime_error("Invalid dimension for vtk read-in."); - } - - // Construct field - scalar_field.emplace_back(name, mesh, gid, 0.0); - } - } - } - - void - read_RL_vtk(const std::string &vtkfile) - { - std::cout << "Begin reading vtk file" << std::endl; - - // read in vtk file here - std::ifstream infile_mesh(vtkfile.c_str()); - - // read mesh info - mesh.read_RL_vtk(infile_mesh); - - std::ifstream infile(vtkfile.c_str()); - - // read point data - std::istringstream ss; - std::string str, name, type, line; - int numcomp; - unsigned long int N_points, Npoints_x, Npoints_y, Npoints_z, Npoints, u, p; - - while (!infile.eof()) - { - std::getline(infile, line); - - if (line[0] == 'X') - { - if (line.size() > 12 && line.substr(0, 13) == "X_COORDINATES") - { - // read header line - // std::cout << line << "\n"; - ss.clear(); - ss.str(line); - ss >> str >> Npoints_x >> type; - - std::cout << "Read X_COORDINATES: " << Npoints_x << std::endl; - - // std::cout << " reserve OK" << std::endl; - } - } - if (line[0] == 'Y') - { - if (line.size() > 12 && line.substr(0, 13) == "Y_COORDINATES") - { - // read header line - // std::cout << line << "\n"; - ss.clear(); - ss.str(line); - ss >> str >> Npoints_y >> type; - - // read points - - std::cout << "Read Y_COORDINATES: " << Npoints_y << std::endl; - - // std::cout << " reserve OK" << std::endl; - } - } - if (line[0] == 'Z') - { - if (line.size() > 12 && line.substr(0, 13) == "Z_COORDINATES") - { - // read header line - // std::cout << line << "\n"; - ss.clear(); - ss.str(line); - ss >> str >> Npoints_z >> type; - - // read points - - std::cout << "Read Z_COORDINATES: " << Npoints_z << std::endl; - - // std::cout << " reserve OK" << std::endl; - } - } - - if (line[0] == 'S') - { - if (line.size() > 6 && line.substr(0, 7) == "SCALARS") - { - ss.clear(); - ss.str(line); - ss >> str >> name >> type >> numcomp; - - // read LOOKUP_TABLE line - std::getline(infile, line); - - // read data - std::cout << "begin reading data" << std::endl; - - N_points = (Npoints_x) * (Npoints_y) * (Npoints_z); - - if (DIM > 2) - { - Npoints = 8 * (Npoints_x - 1) * (Npoints_y - 1) * (Npoints_z - 1); - } - if (DIM == 2) - { - Npoints = 4 * (Npoints_x - 1) * (Npoints_y - 1); - } - - std::vector data( - N_points); // NB: data and gid are of different size - std::vector gid(Npoints); // gid is the grainID for each node - // unsigned int gid[Npoints]; - - for (unsigned int i = 0; i < N_points; i++) - { - infile >> data[i]; - // std::cout << data[i] << std::endl; - } - - std::cout << "beginning grain_id stencil" << std::endl; - - u = 0; - if (DIM > 2) - { - for (unsigned int i = 0; i < (Npoints_z - 1); i++) - { - for (unsigned int j = 0; j < (Npoints_y - 1); j++) - { - for (unsigned int k = 0; k < (Npoints_x - 1); k++) - { - p = k + j * Npoints_x + i * Npoints_x * Npoints_y; - - gid[u] = data[p]; - gid[u + 1] = data[p + 1]; - gid[u + 2] = data[p + Npoints_x]; - gid[u + 3] = data[p + Npoints_x + 1]; - gid[u + 4] = data[p + Npoints_x * Npoints_y]; - gid[u + 5] = data[p + Npoints_x * Npoints_y + 1]; - gid[u + 6] = - data[p + Npoints_x + Npoints_x * Npoints_y]; - gid[u + 7] = - data[p + Npoints_x + Npoints_x * Npoints_y + 1]; - - u += 8; - } - } - } - } - - if (DIM == 2) - { - for (unsigned int j = 0; j < (Npoints_y - 1); j++) - { - for (unsigned int k = 0; k < (Npoints_x - 1); k++) - { - p = k + j * Npoints_x; - - gid[u] = data[p]; - gid[u + 1] = data[p + 1]; - gid[u + 2] = data[p + Npoints_x]; - gid[u + 3] = data[p + Npoints_x + 1]; - - u += 4; - } - } - } - - data.clear(); - - std::cout << "Construct PField '" << name << "'" << std::endl; - scalar_field.push_back( - PField(name, mesh, gid, 0.0)); - std::cout << " done" << std::endl; - - gid.clear(); - } - } - } - - infile.close(); - } - - PField & - find_scalar_field(std::string name) - { - for (unsigned int i = 0; i < scalar_field.size(); i++) - { - if (scalar_field[i].name() == name) - { - return scalar_field[i]; - } - } - throw std::invalid_argument("Could not find scalar_field named '" + name + "'"); - } - }; - - template class Body; - template class Body; - -} // namespace PRISMS - -#endif diff --git a/include/field_input/IntegrationTools/pfield/Coordinate.hh b/include/field_input/IntegrationTools/pfield/Coordinate.hh deleted file mode 100644 index 1c31188e8..000000000 --- a/include/field_input/IntegrationTools/pfield/Coordinate.hh +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef Coordinate_HH -#define Coordinate_HH - -#include - -namespace PRISMS -{ - - /** - * \brief A class for a coordinate, templated by dimension. This is a possible option - * anyplace 'Coordinate' class template is used but it is not the only option. Any class - * that implements 'Coordinate::operator[]()' should work. - */ - template - class Coordinate - { - public: - /** - * \brief Get the number of dimensions. - */ - [[nodiscard]] int - size() const - { - return dim; - } - - /** - * \brief Get the ith coordinate. - */ - float & - operator[](int i) - { - return coordinate[i]; - } - - /** - * \brief Get a const reference to the ith coordinate. - */ - const float & - operator[](int i) const - { - return coordinate[i]; - } - - /** - * \brief Friend function for output stream. - */ - template - friend std::ostream & - operator<<(std::ostream &outstream, const Coordinate &coord); - - private: - float coordinate[dim]; - }; - - template - std::ostream & - operator<<(std::ostream &outstream, const Coordinate &coord) - { - for (int i = 0; i < coord.size(); i++) - { - outstream << coord[i]; - if (i < coord.size() - 1) - { - outstream << " "; - } - } - return outstream; - } -} // namespace PRISMS - -#endif \ No newline at end of file diff --git a/include/field_input/IntegrationTools/pfield/Mesh.hh b/include/field_input/IntegrationTools/pfield/Mesh.hh deleted file mode 100644 index 70bbbd7e5..000000000 --- a/include/field_input/IntegrationTools/pfield/Mesh.hh +++ /dev/null @@ -1,782 +0,0 @@ -#ifndef Mesh_HH -#define Mesh_HH - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace PRISMS -{ - - inline void - construct_basis_function(PFuncBase>, double> *&bfunc, - const std::string &name) - { - AssertThrow(name == "Quad", - dealii::ExcMessage( - "Error in construct_basis_function (2D): unknown name: " + name)); - - bfunc = new Quad(); - } - - inline void - construct_basis_function(PFuncBase>, double> *&bfunc, - const std::string &name) - { - AssertThrow(name == "Hexahedron", - dealii::ExcMessage( - "Error in construct_basis_function (3D): unknown name: " + name)); - - bfunc = new Hexahedron(); - } - - template - void - construct_interpolating_functions( - std::vector *> &interp, - const std::string &name, - unsigned long int cell, - PFuncBase>, double> *bfunc_ptr, - const std::vector &cell_node, - const std::vector> &node) - { - AssertThrow(name == "Quad", - dealii::ExcMessage( - "Error in construct_interpolating_function (2D): unknown name: " + - name)); - - Interpolator *interp_ptr = nullptr; - - PRISMS::Coordinate<2> dim; - dim[0] = node[cell_node[2]][0] - node[cell_node[0]][0]; - dim[1] = node[cell_node[2]][1] - node[cell_node[0]][1]; - - // QuadValues(const Coordinate &node, const Coordinate &dim, int node_index) - for (int j = 0; j < 4; j++) - { - interp.push_back(interp_ptr); - interp.back() = new PRISMS::QuadValues(cell_node[j], - cell, - bfunc_ptr, - node[cell_node[j]], - dim, - j); - } - } - - template - void - construct_interpolating_functions( - std::vector *> &interp, - const std::string &name, - unsigned long int cell, - PFuncBase>, double> *bfunc_ptr, - const std::vector &cell_node, - const std::vector> &node) - { - AssertThrow(name == "Hexahedron", - dealii::ExcMessage( - "Error in construct_interpolating_function (3D): unknown name: " + - name)); - - Interpolator *interp_ptr = nullptr; - - PRISMS::Coordinate<3> dim; - dim[0] = node[cell_node[6]][0] - node[cell_node[0]][0]; - dim[1] = node[cell_node[6]][1] - node[cell_node[0]][1]; - dim[2] = node[cell_node[6]][2] - node[cell_node[0]][2]; - - // QuadValues(const Coordinate &node, const Coordinate &dim, int node_index) - for (int j = 0; j < 8; j++) - { - interp.push_back(interp_ptr); - interp.back() = new PRISMS::HexahedronValues(cell_node[j], - cell, - bfunc_ptr, - node[cell_node[j]], - dim, - j); - } - } - - /// A template class for a finite element mesh - /// Needs: Coordinate::operator[]() for use in Bin - /// - template - class Mesh - { - // min and max coordinate of cuboid surrounding the body - PRISMS::Coordinate _min; - PRISMS::Coordinate _max; - - /// Vector of nodal coordinates - /// nodal values live in 'Field' class - /// - std::vector> _node; - - /// array containing interpolating functions: - /// owns the interpolating functions - /// interpolating functions contain basis function / element info, - /// these point to _bfunc pfunctions which are used to evaluate - /// - std::vector *> _interp; - - /// array containing PFunctions evaluated by interpolating functions - /// owns the pfunctions, which are pointed to by the interpolating functions - /// !!! do not modify after initial construction or pointers will be messed up !!! - /// - std::vector>, double> *> _bfunc; - - /// bin of interpolating functions (this might be updated to be either Element or - /// Spline Bins) - /// - Bin *, Coordinate> _bin; - - public: - Mesh() = default; - - ~Mesh() - { - for (unsigned int i = 0; i < _interp.size(); i++) - { - delete _interp[i]; - } - - for (unsigned int i = 0; i < _bfunc.size(); i++) - { - delete _bfunc[i]; - } - }; - - void - read_vtk(std::ifstream &infile) - { - std::istringstream ss; - std::string line; - std::string str; - std::string type; - - unsigned int uli_dummy = 0; - double d_dummy = NAN; - - unsigned long int Npoints = 0; - unsigned long int Ncells = 0; - unsigned long int Ncell_numbers = 0; - std::vector cell_node; - - PRISMS::Coordinate coord; - - std::vector min; - std::vector N; - std::vector incr; - - while (std::getline(infile, line)) - { - // read POINTS info: - if (line.compare(0, 6, "POINTS") == 0) - { - // read header line - ss.clear(); - ss.str(line); - ss >> str >> Npoints >> type; - - // read points - std::vector> value(dim); - std::vector> hist(dim); - - _node.reserve(Npoints); - for (unsigned int i = 0; i < Npoints; i++) - { - if (dim == 2) - { - infile >> coord[0] >> coord[1] >> d_dummy; - } - else if (dim == 3) - { - infile >> coord[0] >> coord[1] >> coord[2]; - } - - for (int j = 0; j < dim; j++) - { - add_once(value[j], hist[j], coord[j]); - } - _node.push_back(coord); - } - - // create bins - for (int j = 0; j < dim; j++) - { - std::sort(value[j].begin(), value[j].end()); - min.push_back(value[j][0]); - N.push_back(value[j].size()); - incr.push_back((value[j].back() - value[j][0]) / (1.0 * N.back())); - - // get min and max surrounding coordinates - _min[j] = value[j][0]; - _max[j] = value[j].back(); - - // for short term, expand bin to avoid edge issues - min[j] -= incr[j]; - N[j] += 2; - } - - _bin = Bin *, Coordinate>(min, incr, N); - } - - if (line.compare(0, 5, "CELLS") == 0) - { - ss.clear(); - ss.str(line); - - ss >> str >> Ncells >> Ncell_numbers; - - PFuncBase>, double> *bfunc_ptr = - nullptr; - _bfunc.push_back(bfunc_ptr); - - if (dim == 2) - { - // add Quad basis function - _interp.reserve(Ncells * 4); - construct_basis_function(_bfunc.back(), "Quad"); - } - else if (dim == 3) - { - // add Hexahedron basis function - _interp.reserve(Ncells * 8); - construct_basis_function(_bfunc.back(), "Hexahedron"); - } - bfunc_ptr = _bfunc.back(); - - for (unsigned int i = 0; i < Ncells; i++) - { - infile >> uli_dummy; - - cell_node.resize(uli_dummy); - for (unsigned int j = 0; j < uli_dummy; j++) - { - infile >> cell_node[j]; - } - - // create interpolator - if (dim == 2) - { - construct_interpolating_functions(_interp, - "Quad", - i, - bfunc_ptr, - cell_node, - _node); - } - else if (dim == 3) - { - construct_interpolating_functions(_interp, - "Hexahedron", - i, - bfunc_ptr, - cell_node, - _node); - } - } - - // bin interpolators - for (unsigned int i = 0; i < _interp.size(); i++) - { - _bin.add_range(_interp[i], _interp[i]->min(), _interp[i]->max()); - } - } - else if (line.size() > 9 && line.substr(0, 10) == "CELL_TYPES") - { - // std::cout << line << "\n"; - ss.clear(); - ss.str(line); - - // std::cout << "ss.str()" << ss.str() << std::endl; - ss >> str >> Ncells; - - for (unsigned int i = 0; i < Ncells; i++) - { - infile >> uli_dummy; - - if (uli_dummy != 9 && uli_dummy != 12) - { - std::cout << "Error reading CELL_TYPES: CELL TYPE != 9 && != 12" - << std::endl; - std::cout << " CELL TYPE: " << uli_dummy << std::endl; - exit(1); - } - } - - return; - } - } - } - - // main code reading the RL coordinates and constructing cells and connectivity - // similar to unstructured grid - void - read_RL_vtk(std::ifstream &infile) - { - std::cout << "Read rectilinear file and create mesh" << std::endl; - - bool mesh_as_points = true; - std::vector x_coord; - std::vector y_coord; - std::vector z_coord; - - std::istringstream ss; - std::string line; - std::string str; - std::string type; - - unsigned int uli_dummy = 0; - - unsigned long int Npoints = 0; - unsigned long int Ncells = 0; - unsigned long int u = 0; - std::vector cell_node; - - PRISMS::Coordinate _coord; - - std::vector min; - std::vector N; - std::vector incr; - - while (!infile.eof()) - { - std::getline(infile, line); - // std::cout << "line: " << line << std::endl; - if (line[0] == 'X') - { - if (line.size() > 12 && line.substr(0, 13) == "X_COORDINATES") - { - // read header line - // std::cout << line << "\n"; - ss.clear(); - ss.str(line); - ss >> str >> Npoints >> type; - - std::cout << "Read X_COORDINATES: " << Npoints << std::endl; - std::cout << " reserve OK" << std::endl; - for (unsigned int i = 0; i < Npoints; i++) - { - float temp_coord = NAN; - - infile >> temp_coord; - - x_coord.push_back(temp_coord); - } - } - } - if (line[0] == 'Y') - { - if (line.size() > 12 && line.substr(0, 13) == "Y_COORDINATES") - { - // read header line - // std::cout << line << "\n"; - ss.clear(); - ss.str(line); - ss >> str >> Npoints >> type; - - // read points - - std::cout << "Read Y_COORDINATES: " << Npoints << std::endl; - - std::cout << " reserve OK" << std::endl; - for (unsigned int i = 0; i < Npoints; i++) - { - float temp_coord = NAN; - - infile >> temp_coord; - - y_coord.push_back(temp_coord); - } - } - } - if (line[0] == 'Z') - { - if (line.size() > 12 && line.substr(0, 13) == "Z_COORDINATES") - { - // read header line - // std::cout << line << "\n"; - ss.clear(); - ss.str(line); - ss >> str >> Npoints >> type; - - // read points - - std::cout << "Read Z_COORDINATES: " << Npoints << std::endl; - - std::cout << " reserve OK" << std::endl; - for (unsigned int i = 0; i < Npoints; i++) - { - float temp_coord = NAN; - - infile >> temp_coord; - - z_coord.push_back(temp_coord); - } - } - } - } - - if (mesh_as_points) - { - std::vector> value(dim); - std::vector> hist(dim); - - if (dim > 2) - { - Npoints = - 8 * (x_coord.size() - 1) * (y_coord.size() - 1) * (z_coord.size() - 1); - } - if (dim == 2) - { - Npoints = 4 * (x_coord.size() - 1) * (y_coord.size() - 1); - } - - // interpolated coordinates for each node of a cell - std::vector COORD_X(Npoints); - std::vector COORD_Y(Npoints); - std::vector COORD_Z(Npoints); - - u = 0; //(defined at the beginning of read_vtk) - if (dim > 2) - { - for (unsigned int i = 0; i < (z_coord.size() - 1); i++) - { - for (unsigned int j = 0; j < (y_coord.size() - 1); j++) - { - for (unsigned int k = 0; k < (x_coord.size() - 1); k++) - { - COORD_X[u] = x_coord.at(k); - COORD_Y[u] = y_coord.at(j); - COORD_Z[u] = z_coord.at(i); - - COORD_X[u + 1] = x_coord.at(k + 1); - COORD_Y[u + 1] = y_coord.at(j); - COORD_Z[u + 1] = z_coord.at(i); - - COORD_X[u + 2] = x_coord.at(k); - COORD_Y[u + 2] = y_coord.at(j + 1); - COORD_Z[u + 2] = z_coord.at(i); - - COORD_X[u + 3] = x_coord.at(k + 1); - COORD_Y[u + 3] = y_coord.at(j + 1); - COORD_Z[u + 3] = z_coord.at(i); - - COORD_X[u + 4] = x_coord.at(k); - COORD_Y[u + 4] = y_coord.at(j); - COORD_Z[u + 4] = z_coord.at(i + 1); - - COORD_X[u + 5] = x_coord.at(k + 1); - COORD_Y[u + 5] = y_coord.at(j); - COORD_Z[u + 5] = z_coord.at(i + 1); - - COORD_X[u + 6] = x_coord.at(k); - COORD_Y[u + 6] = y_coord.at(j + 1); - COORD_Z[u + 6] = z_coord.at(i + 1); - - COORD_X[u + 7] = x_coord.at(k + 1); - COORD_Y[u + 7] = y_coord.at(j + 1); - COORD_Z[u + 7] = z_coord.at(i + 1); - - u += 8; - } - } - } - } - - if (dim == 2) - { - for (unsigned int j = 0; j < (y_coord.size() - 1); j++) - { - for (unsigned int k = 0; k < (x_coord.size() - 1); k++) - { - COORD_X[u] = x_coord.at(k); - COORD_Y[u] = y_coord.at(j); - COORD_Z[u] = z_coord.at(0); - - COORD_X[u + 1] = x_coord.at(k + 1); - COORD_Y[u + 1] = y_coord.at(j); - COORD_Z[u + 1] = z_coord.at(0); - - COORD_X[u + 2] = x_coord.at(k); - COORD_Y[u + 2] = y_coord.at(j + 1); - COORD_Z[u + 2] = z_coord.at(0); - - COORD_X[u + 3] = x_coord.at(k + 1); - COORD_Y[u + 3] = y_coord.at(j + 1); - COORD_Z[u + 3] = z_coord.at(0); - - u += 4; - } - } - } - - // _node.reserve(Npoints); - for (unsigned int i = 0; i < Npoints; i++) - { - _coord[0] = COORD_X[i]; - _coord[1] = COORD_Y[i]; - if (dim > 2) - { - _coord[2] = COORD_Z[i]; - } - - for (int m = 0; m < dim; m++) - { - add_once(value[m], hist[m], _coord[m]); - } - - _node.push_back(_coord); - } - - x_coord.clear(); - y_coord.clear(); - z_coord.clear(); - COORD_X.clear(); - COORD_Y.clear(); - COORD_Z.clear(); - - std::cout << "point coordinates done" << std::endl; - - std::cout << "Determine Body size" << std::endl; - for (int j = 0; j < dim; j++) - { - std::sort(value[j].begin(), value[j].end()); - // std::cout << "j: " << j << " back(): " << value[j].back() << std::endl; - min.push_back(value[j][0]); - N.push_back(value[j].size()); - incr.push_back((value[j].back() - value[j][0]) / (1.0 * N.back())); - - // get min and max surrounding coordinates - _min[j] = value[j][0]; - _max[j] = value[j].back(); - - // for short term, expand bin to avoid edge issues - min[j] -= incr[j]; - N[j] += 2; - } - std::cout << " Min Coordinate: "; - for (int j = 0; j < dim; j++) - { - std::cout << _min[j] << " "; - } - std::cout << std::endl; - std::cout << " Max Coordinate: "; - for (int j = 0; j < dim; j++) - { - std::cout << _max[j] << " "; - } - std::cout << std::endl; - - std::cout << " done" << std::endl; - - std::cout << "Initialize Bin" << std::endl; - _bin = Bin *, Coordinate>(min, incr, N); - std::cout << " done" << std::endl; - - // Now add the cell data - // unsigned int Ncells = (x_coord.size()-1) * (y_coord.size()-1); - if (dim == 2) - { - Ncells = (_node.size() / 4); - } - else if (dim > 2) - { - Ncells = (_node.size() / 8); - } - - PFuncBase>, double> *bfunc_ptr; - _bfunc.push_back(bfunc_ptr); - - if (dim == 2) - { - // add Quad basis function - // _interp.reserve(Ncells*4); - construct_basis_function(_bfunc.back(), "Quad"); - } - else if (dim > 2) - { - // add Hexahedron basis function - // _interp.reserve(Ncells*8); - construct_basis_function(_bfunc.back(), "Hexahedron"); - } - bfunc_ptr = _bfunc.back(); - - std::cout << "Read CELLS: " << Ncells << std::endl; - - if (dim > 2) - { - uli_dummy = 8; - } - else - { - uli_dummy = 4; - } - - cell_node.reserve(uli_dummy); - for (unsigned int i = 0; i < Ncells; i++) - { - for (unsigned int j = 0; j < uli_dummy; j++) - { - cell_node[j] = i * uli_dummy + j; - } - - if (dim == 2) - { - std::swap(cell_node[2], cell_node[3]); - } - if (dim > 2) - { - std::swap(cell_node[2], cell_node[3]); - std::swap(cell_node[6], cell_node[7]); - } - // std::cout << cell_node[0] << " " << cell_node[1] << " " << cell_node[2] - // << " " << cell_node[3] << std::endl; - - // create interpolator - if (dim == 2) - { - construct_interpolating_functions(_interp, - "Quad", - i, - bfunc_ptr, - cell_node, - _node); - } - else if (dim > 2) - { - construct_interpolating_functions(_interp, - "Hexahedron", - i, - bfunc_ptr, - cell_node, - _node); - } - } - std::cout << "cell creation done" << std::endl; - - // bin interpolators - std::cout << "Bin interpolating functions" << std::endl; - std::cout << "num nodes: " << _node.size() << std::endl; - for (unsigned int i = 0; i < _interp.size(); i++) - { - // std::cout << "interp: " << _interp[i] << " " << _interp[i]->min() << " " - // << _interp[i]->max() << std::endl; - _bin.add_range(_interp[i], _interp[i]->min(), _interp[i]->max()); - } - std::cout << " done max_bin_size: " << _bin.max_size() << std::endl; - } - } - - void - min(Coordinate &coord) - { - for (int i = 0; i < dim; i++) - { - coord[i] = _min[i]; - } - } - - void - max(Coordinate &coord) - { - for (int i = 0; i < dim; i++) - { - coord[i] = _max[i]; - } - } - - double - min(int i) - { - return _min[i]; - } - - double - max(int i) - { - return _max[i]; - } - - int - max_bin_size() - { - return _bin.max_size(); - } - - // Set 'bfunc' to evaluated basis functions at 'coord', - // 'node_index' to node indices for each basis function, - // and 's' is the length (number of basis functions) - // - 'bfunc' and 'node_index' are not resized, they must be big enough - // - void - basis_functions(const Coordinate &coord, - std::vector &bfunc, - std::vector &node_index, - int &s) - { - std::vector *> &bin = _bin.contents(coord); - s = bin.size(); - - int i = 0; - unsigned long int element; - - for (i = 0; i < s; i++) - { - if ((*bin[i]).is_in_range(coord)) - { - element = (*bin[i]).element(); - for (i = 0; i < s; i++) - { - if ((*bin[i]).element() == element) - { - bfunc[i] = (*bin[i])(coord); - } - else - { - bfunc[i] = 0.0; - } - node_index[i] = (*bin[i]).node(); - } - return; - } - // else - //{ - // bfunc[i] = 0.0; - // node_index[i] = (*bin[i]).node(); - // } - } - }; - - private: - void - add_once(std::vector &list, std::vector &hist, double val) - { - for (unsigned int i = 0; i < list.size(); i++) - { - if (list[i] == val) - { - hist[i]++; - return; - } - } - - list.push_back(val); - hist.push_back(1); - } - }; -} // namespace PRISMS - -#endif diff --git a/include/field_input/IntegrationTools/pfield/PField.hh b/include/field_input/IntegrationTools/pfield/PField.hh deleted file mode 100644 index 029a7dc97..000000000 --- a/include/field_input/IntegrationTools/pfield/PField.hh +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef PField_HH -#define PField_HH - -#include -#include - -//_name(name), -//_var_name(var_name), -//_var_description(var_description), - -namespace PRISMS -{ - - /// A class for a field - /// - /// Varcontainer contains variables, for instance 'std::vector' for x, y, z - /// FieldType is the datatype for the field, - /// for instance 'double' for temperature or 'std::vector' for vector - /// displacement - /// - /// A field consists of a pointer to a mesh, a list field values (at mesh nodes) - template - class PField : public PFuncBase - { - public: - using size_type = typename PFuncBase::size_type; - - // pointer to a Mesh that lives in a Body - Mesh *_mesh; - - // array of field values at mesh nodes - std::vector _field; - - FieldType _zero; - - FieldType _val; - - // ---------------------------------------------------------- - // Constructors - // PField(); - - PField(const std::string &name, - Mesh &mesh, - const std::vector &field, - const FieldType &zero) - : PFuncBase(name) - , _mesh(&mesh) - , _field(field) - , _zero(zero) - - { - int max = mesh.max_bin_size(); - _bfunc.resize(max); - _node_index.resize(max); - } - - // ---------------------------------------------------------- - // Use these functions if you want to evaluate a single value - FieldType - operator()(const Coordinate &coord) override; - - // ---------------------------------------------------------- - // Use these functions to evaluate several values, then use 'get' methods to access - // results - void - eval(const Coordinate &coord) override; - - FieldType - operator()() const override; - - // PField unique members ------------------------------------------ - - private: - // temporary vector - std::vector _bfunc; - std::vector _node_index; - int _Nbfunc; - }; - - template - FieldType - PField::operator()(const Coordinate &coord) - { - // get evaluated basis functions - (*_mesh).basis_functions(coord, _bfunc, _node_index, _Nbfunc); - - // sum them - _val = _zero; - for (int i = 0; i < _Nbfunc; i++) - { - _val += _bfunc[i] * _field[_node_index[i]]; - } - - return _val; - } - - template - void - PField::eval(const Coordinate &coord) - { - (*this)(coord); - } - - template - FieldType - PField::operator()() const - { - return _val; - } - -} // namespace PRISMS - -#endif diff --git a/include/field_input/IntegrationTools/pfield/interpolation/Hexahedron.hh b/include/field_input/IntegrationTools/pfield/interpolation/Hexahedron.hh deleted file mode 100644 index d65b41aec..000000000 --- a/include/field_input/IntegrationTools/pfield/interpolation/Hexahedron.hh +++ /dev/null @@ -1,238 +0,0 @@ -#ifndef Hexahedron_HH -#define Hexahedron_HH - -#include -#include -#include -#include - -namespace PRISMS -{ - class Hexahedron_f : public PSimpleBase>, double> - { - [[nodiscard]] double - eval(const std::vector> &var) const override - { - // var[0]: coordinate to be evaluated - // var[1]: nodal coordinate - // var[2]: element dimension - // var[3]: +/- 1 depending on which 'corner' of quad - // - // f = (1.0 - e0)*(1.0 - e1)*(1.0 - e2) - // e = var[3]*(var[0] - var[1])/var[2] - - return (1.0 - var[3][0] * (var[0][0] - var[1][0]) / var[2][0]) * - (1.0 - var[3][1] * (var[0][1] - var[1][1]) / var[2][1]) * - (1.0 - var[3][2] * (var[0][2] - var[1][2]) / var[2][2]); - } - - public: - Hexahedron_f() - { - this->_name = "Hexahedron_f"; - } - }; - - class Hexahedron : public PFuncBase>, double> - { - PSimpleBase>, double> *_val; - - public: - Hexahedron() - : PFuncBase>, double>() - { - _val = new Hexahedron_f(); - } - - ~Hexahedron() - { - delete _val; - } - - double - operator()(const std::vector> &var) override - { - return (*_val)(var); - } - - void - eval(const std::vector> &var) override - { - (*_val)(var); - } - - double - operator()() const override - { - return (*_val)(); - } - }; - - /// A base class for interpolating functions - /// - template - class HexahedronValues : public Interpolator - { - //_var[0]: Coordinate _r; // coordinate to evaluate field at - //_var[1]: Coordinate _n; // coordinate of node - //_var[2]: Coordinate _h; // quad dimensions - //_var[3]: Coordinate _s; // +/- 1, depending on orientation of basis function - std::vector> _var; - - public: - using size_type = typename Interpolator::size_type; - - // node_index: index of node in mesh - // node_index: index of element in mesh - // bfunc: PFuncBase, double>* - // node_coord: Coordinate of node - // dim: Coordinate containing x and y dimension of element - // element_node_index: 0 == bottom left, proceed counter clockwise to 3 == top left of - // element - - HexahedronValues(unsigned long int node_index, - unsigned long int element_index, - PFuncBase>, double> *bfunc, - const PRISMS::Coordinate<3> &node_coord, - const PRISMS::Coordinate<3> &dim, - int element_node_index) - : Interpolator(node_index, element_index, bfunc) - { - _var.resize(4); - - _var[1][0] = node_coord[0]; - _var[1][1] = node_coord[1]; - _var[1][2] = node_coord[2]; - - _var[2][0] = dim[0]; - _var[2][1] = dim[1]; - _var[2][2] = dim[2]; - - if (element_node_index == 0) - { - _var[3][0] = 1.0; - _var[3][1] = 1.0; - _var[3][2] = 1.0; - } - else if (element_node_index == 1) - { - _var[3][0] = -1.0; - _var[3][1] = 1.0; - _var[3][2] = 1.0; - } - else if (element_node_index == 2) - { - _var[3][0] = -1.0; - _var[3][1] = -1.0; - _var[3][2] = 1.0; - } - else if (element_node_index == 3) - { - _var[3][0] = 1.0; - _var[3][1] = -1.0; - _var[3][2] = 1.0; - } - else if (element_node_index == 4) - { - _var[3][0] = 1.0; - _var[3][1] = 1.0; - _var[3][2] = -1.0; - } - else if (element_node_index == 5) - { - _var[3][0] = -1.0; - _var[3][1] = 1.0; - _var[3][2] = -1.0; - } - else if (element_node_index == 6) - { - _var[3][0] = -1.0; - _var[3][1] = -1.0; - _var[3][2] = -1.0; - } - else if (element_node_index == 7) - { - _var[3][0] = 1.0; - _var[3][1] = -1.0; - _var[3][2] = -1.0; - } - } - - [[nodiscard]] PRISMS::Coordinate<3> - min() const override - { - PRISMS::Coordinate<3> coord = _var[1]; - - if (_var[3][0] == -1.0) - { - coord[0] -= _var[2][0]; - } - if (_var[3][1] == -1.0) - { - coord[1] -= _var[2][1]; - } - if (_var[3][2] == -1.0) - { - coord[2] -= _var[2][2]; - } - - return coord; - } - - [[nodiscard]] PRISMS::Coordinate<3> - max() const override - { - PRISMS::Coordinate<3> coord = _var[1]; - - if (_var[3][0] == 1.0) - { - coord[0] += _var[2][0]; - } - if (_var[3][1] == 1.0) - { - coord[1] += _var[2][1]; - } - if (_var[3][2] == 1.0) - { - coord[2] += _var[2][2]; - } - - return coord; - } - - bool - is_in_range(const Coordinate &coord) override - { - _var[0][0] = coord[0]; - _var[0][1] = coord[1]; - _var[0][2] = coord[2]; - double e; - - for (int i = 0; i < 3; i++) - { - e = _var[3][i] * (_var[0][i] - _var[1][i]) / _var[2][i]; - if (e < 0.0 || e >= 1.0) - { - return false; - } - } - - return true; - } - - // for the following, - // you are expected to KNOW that the coord is_in_range!!! - - double - operator()(const Coordinate &coord) override - { - _var[0][0] = coord[0]; - _var[0][1] = coord[1]; - _var[0][2] = coord[2]; - return (*this->_bfunc)(_var); - } - }; - -} // namespace PRISMS - -#endif diff --git a/include/field_input/IntegrationTools/pfield/interpolation/Interpolator.hh b/include/field_input/IntegrationTools/pfield/interpolation/Interpolator.hh deleted file mode 100644 index 911f8a2ba..000000000 --- a/include/field_input/IntegrationTools/pfield/interpolation/Interpolator.hh +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef Interpolator_HH -#define Interpolator_HH - -#include -#include - -namespace PRISMS -{ - - /// A base class for interpolating functions - /// - template - class Interpolator - { - protected: - unsigned long int _node; // index of nodal value or control point - unsigned long int _element; // index of element - PFuncBase>, double> *_bfunc; // basis function to - // evaluate - - public: - using size_type = - typename PFuncBase>, double>::size_type; - - Interpolator(unsigned long int node, - unsigned long int element, - PFuncBase>, double> *bfunc) - : _node(node) - , _element(element) - , _bfunc(bfunc) {}; - - virtual ~Interpolator() {}; - - unsigned long int - node() - { - return _node; - } - - unsigned long int - element() - { - return _element; - } - - virtual PRISMS::Coordinate - min() const - { - undefined("void min(Coordinate &coord) const"); - return PRISMS::Coordinate(); - } - - virtual PRISMS::Coordinate - max() const - { - undefined("void max(Coordinate &coord) const"); - return PRISMS::Coordinate(); - } - - virtual bool - is_in_range([[maybe_unused]] const Coordinate &coord) - { - undefined("bool is_in_range(Coordinate coord) const"); - return false; - } - - virtual double - operator()([[maybe_unused]] const Coordinate &coord) - { - undefined("double operator()(Coordinate coord)"); - return double(); - } - - private: - void - undefined(std::string fname) const - { - std::cout << "Error in Interpolator." << std::endl; - std::cout << " The member function '" << fname << "' has not been defined." - << std::endl; - exit(1); - } - }; - -} // namespace PRISMS - -#endif diff --git a/include/field_input/IntegrationTools/pfield/interpolation/Quad.hh b/include/field_input/IntegrationTools/pfield/interpolation/Quad.hh deleted file mode 100644 index 199a8bb37..000000000 --- a/include/field_input/IntegrationTools/pfield/interpolation/Quad.hh +++ /dev/null @@ -1,197 +0,0 @@ -#ifndef Quad_HH -#define Quad_HH - -#include -#include -#include -#include - -namespace PRISMS -{ - class Quad_f : public PSimpleBase>, double> - { - [[nodiscard]] double - eval(const std::vector> &var) const override - { - // var[0]: coordinate to be evaluated - // var[1]: nodal coordinate - // var[2]: element dimension - // var[3]: +/- 1 depending on which 'corner' of quad - // - // f = (1.0 - e0)*(1.0 - e1) - // e = var[3]*(var[0] - var[1])/var[2] - - return (1.0 - var[3][0] * (var[0][0] - var[1][0]) / var[2][0]) * - (1.0 - var[3][1] * (var[0][1] - var[1][1]) / var[2][1]); - } - - public: - Quad_f() - { - this->_name = "Quad_f"; - } - }; - - class Quad : public PFuncBase>, double> - { - PSimpleBase>, double> *_val; - - public: - using size_type = PFuncBase>, double>::size_type; - - Quad() - : PFuncBase>, double>() - { - _val = new Quad_f(); - } - - ~Quad() - { - delete _val; - } - - double - operator()(const std::vector> &var) override - { - return (*_val)(var); - } - - void - eval(const std::vector> &var) override - { - (*_val)(var); - } - - double - operator()() const override - { - return (*_val)(); - } - }; - - /// A base class for interpolating functions - /// - template - class QuadValues : public Interpolator - { - //_var[0]: Coordinate _r; // coordinate to evaluate field at - //_var[1]: Coordinate _n; // coordinate of node - //_var[2]: Coordinate _h; // quad dimensions - //_var[3]: Coordinate _s; // +/- 1, depending on orientation of basis function - std::vector> _var; - - public: - using size_type = typename Interpolator::size_type; - - // node_index: index of node in mesh - // node_index: index of element in mesh - // bfunc: PFuncBase, double>* - // node_coord: Coordinate of node - // dim: Coordinate containing x and y dimension of element - // element_node_index: 0 == bottom left, proceed counter clockwise to 3 == top left of - // element - - QuadValues(unsigned long int node_index, - unsigned long int element_index, - PFuncBase>, double> *bfunc, - const PRISMS::Coordinate<2> &node_coord, - const PRISMS::Coordinate<2> &dim, - int element_node_index) - : Interpolator(node_index, element_index, bfunc) - { - _var.resize(4); - - _var[1][0] = node_coord[0]; - _var[1][1] = node_coord[1]; - - _var[2][0] = dim[0]; - _var[2][1] = dim[1]; - - if (element_node_index == 0) - { - _var[3][0] = 1.0; - _var[3][1] = 1.0; - } - else if (element_node_index == 1) - { - _var[3][0] = -1.0; - _var[3][1] = 1.0; - } - else if (element_node_index == 2) - { - _var[3][0] = -1.0; - _var[3][1] = -1.0; - } - else if (element_node_index == 3) - { - _var[3][0] = 1.0; - _var[3][1] = -1.0; - } - } - - [[nodiscard]] PRISMS::Coordinate<2> - min() const override - { - PRISMS::Coordinate<2> coord = _var[1]; - - if (_var[3][0] == -1.0) - { - coord[0] -= _var[2][0]; - } - if (_var[3][1] == -1.0) - { - coord[1] -= _var[2][1]; - } - - return coord; - } - - [[nodiscard]] PRISMS::Coordinate<2> - max() const override - { - PRISMS::Coordinate<2> coord = _var[1]; - - if (_var[3][0] == 1.0) - { - coord[0] += _var[2][0]; - } - if (_var[3][1] == 1.0) - { - coord[1] += _var[2][1]; - } - - return coord; - } - - bool - is_in_range(const Coordinate &coord) override - { - _var[0][0] = coord[0]; - _var[0][1] = coord[1]; - - for (int i = 0; i < 2; i++) - { - double e = _var[3][i] * (_var[0][i] - _var[1][i]) / _var[2][i]; - if (e < 0.0 || e >= 1.0) - { - return false; - } - } - - return true; - } - - // for the following, you are expected to KNOW that the coord is_in_range!!! - - double - operator()(const Coordinate &coord) override - { - _var[0][0] = coord[0]; - _var[0][1] = coord[1]; - return (*this->_bfunc)(_var); - } - }; - -} // namespace PRISMS - -#endif \ No newline at end of file diff --git a/include/field_input/IntegrationTools/pfunction/PFuncBase.hh b/include/field_input/IntegrationTools/pfunction/PFuncBase.hh deleted file mode 100644 index 6b5bb8de0..000000000 --- a/include/field_input/IntegrationTools/pfunction/PFuncBase.hh +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef PFuncBase_HH -#define PFuncBase_HH - -#include -#include -#include -#include -#include - -namespace PRISMS -{ - /// A Base class for a function, including grad & hess - /// - template - class PFuncBase - { - protected: - std::string _name; - - public: - using size_type = std::vector::size_type; - - PFuncBase() = default; - - PFuncBase(const std::string &name) - : _name(name) - {} - - virtual ~PFuncBase() - {} - - std::string - name() - { - return _name; - } - - // ---------------------------------------------------------- - // Use these functions if you want to evaluate a single value - virtual OutType - operator()([[maybe_unused]] const VarContainer &var) - { - undefined("OutType operator()(const VarContainer &var)"); - return OutType(); - } - - // ---------------------------------------------------------- - // Use these functions to evaluate several values, then use 'get' methods to access - // results - virtual void - eval([[maybe_unused]] const VarContainer &var) - { - undefined("void eval_grad( const VarContainer &var)"); - } - - virtual OutType - operator()() const - { - undefined("OutType operator()"); - return OutType(); - } - - private: - void - undefined(std::string fname) const - { - std::string msg = "Error in PFuncBase '" + _name + "'.\n" + - " The member function '" + fname + "' has not been defined.\n"; - throw std::runtime_error(msg); - } - }; - -} // namespace PRISMS - -#endif \ No newline at end of file diff --git a/include/field_input/IntegrationTools/pfunction/PSimpleBase.hh b/include/field_input/IntegrationTools/pfunction/PSimpleBase.hh deleted file mode 100644 index 31746d121..000000000 --- a/include/field_input/IntegrationTools/pfunction/PSimpleBase.hh +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef PSimpleBase_HH -#define PSimpleBase_HH - -#include -#include -#include -#include -#include - -namespace PRISMS -{ - - /// Base classes for functions that can be hard-coded, - /// then shared and used elsewhere - - /// A simple expression evaluator - /// - template - class PSimpleBase - { - protected: - std::string _name; - OutType _val; - - public: - virtual ~PSimpleBase() {}; - - [[nodiscard]] std::string - name() const - { - return _name; - } - - OutType - operator()(const VarContainer &var) - { - return _val = eval(var); - } - - OutType - operator()() const - { - return _val; - } - - private: - virtual OutType - eval([[maybe_unused]] const VarContainer &var) const - { - undefined("OutType eval( const VarContainer &var)"); - return OutType(); - } - - void - undefined(std::string fname) const - { - std::string msg = "Error in PSimpleBase '" + _name + "'.\n" + - " The member function '" + fname + "' has not been defined.\n"; - throw std::runtime_error(msg); - } - }; - -} // namespace PRISMS - -#endif \ No newline at end of file diff --git a/include/field_input/field_input.h b/include/field_input/field_input.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/grains/FloodFiller.h b/include/grains/FloodFiller.h deleted file mode 100644 index 2b07a6959..000000000 --- a/include/grains/FloodFiller.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef INCLUDE_FLOODFILLER_H_ -#define INCLUDE_FLOODFILLER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * This class holds information for a grain, including its index and the list of - * vertices in that grain. - */ -template -class Grain -{ -public: - /** - * Sets the grain index. - */ - void - setGrainIndex(unsigned int _grain_index) - { - grain_index = _grain_index; - }; - - /** - * Gets the grain index. - */ - [[nodiscard]] unsigned int - getGrainIndex() const - { - return grain_index; - }; - - /** - * Sets the order parameter index. - */ - void - setOrderParameterIndex(unsigned int _order_parameter_index) - { - order_parameter_index = _order_parameter_index; - }; - - /** - * Gets the order parameter index. - */ - [[nodiscard]] unsigned int - getOrderParameterIndex() const - { - return order_parameter_index; - }; - - /** - * Adds the vertices of a new element to the list. - */ - void - addVertexList(std::vector> _vertices) - { - list_of_vertices.push_back(_vertices); - }; - - /** - * Gets the entire list of elements and the list of vertices per element. - */ - std::vector>> - getVertexList() const - { - return list_of_vertices; - }; - -private: - /** - * The grain index. - */ - unsigned int grain_index; - - /** - * The variable index for the order parameter containing this grain. - */ - unsigned int order_parameter_index; - - /** - * A vector of the elements in the grain containing a vector of the vertices - * for each element. - */ - std::vector>> list_of_vertices; -}; - -/** - * This class uses a recursive flood filling algorithm to find connected bodies - * in a field, given a threshold. The MPI communication methods are similar to - * those in parallelNucleationList. - */ -template -class FloodFiller -{ -public: - /** - * Constructor. - */ - FloodFiller(dealii::FESystem &_fe, dealii::QGaussLobatto _quadrature) - : quadrature(_quadrature) - , num_quad_points(_quadrature.size()) - , dofs_per_cell(_fe.dofs_per_cell) - , fe(&_fe) {}; - - /** - * The primary external interface. This method takes in information about the - * mesh/field and outputs a vector of Grain objects. - */ - void - calcGrainSets(dealii::FESystem &finite_element, - dealii::DoFHandler &dof_handler, - dealii::LinearAlgebra::distributed::Vector *solution_field, - double threshold_lower, - double threshold_upper, - int min_id, - unsigned int order_parameter_index, - std::vector> &grain_sets); - -protected: - /** - * The actual recursive flood fill method. - */ - template - void - recursiveFloodFill(T cell, - T cell_end, - dealii::LinearAlgebra::distributed::Vector *solution_field, - double threshold_lower, - double threshold_upper, - int min_id, - unsigned int &grain_index, - std::vector> &grain_sets, - bool &grain_assigned); - - /** - * The method to merge the grain sets from all the processors. - */ - void - createGlobalGrainSetList(std::vector> &grain_sets) const; - - /** - * Checks to see if grains found on different processors are parts of a larger - * grain. If so, it merges the grain_sets entries. - */ - void - mergeSplitGrains(std::vector> &grain_sets) const; - - /** - * The quadrature used to calculate the element-wise value of the solution - * field. - */ - dealii::QGaussLobatto quadrature; - - /** - * The number of quadrature points per cell. - */ - const unsigned int num_quad_points; - - /** - * The number of degrees of freedom per cell. - */ - const unsigned int dofs_per_cell; - - /** - * The deal.II finite element object, set in the constructor. - */ - dealii::FESystem *fe; -}; - -#endif diff --git a/include/grains/OrderParameterRemapper.h b/include/grains/OrderParameterRemapper.h deleted file mode 100644 index a935ff6fb..000000000 --- a/include/grains/OrderParameterRemapper.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef INCLUDE_ORDERPARAMETERREMAPPER_H_ -#define INCLUDE_ORDERPARAMETERREMAPPER_H_ - -#include -#include - -#include -#include - -/** - * This class uses information from the list of SimplifiedGrainRepresentation - * objects to reassign grains across multiple solution fields. - */ -template -class OrderParameterRemapper -{ -public: - /** - * This method does the core work of the class to reassign grains across - * solution vectors based on the list of SimplifiedGrainRepresentation - * objects. - */ - void - remap( - std::vector> &grain_representations, - std::vector *> &solution_fields, - dealii::DoFHandler &dof_handler, - unsigned int dofs_per_cell); - - /** - * This method does the core work of the class to reassign grains across - * solution vectors based on the list of SimplifiedGrainRepresentation - * objects. - */ - void - remap_from_index_field( - std::vector> &grain_representations, - const dealii::LinearAlgebra::distributed::Vector *grain_index_field, - std::vector *> &solution_fields, - dealii::DoFHandler &dof_handler, - unsigned int dofs_per_cell); - -protected: -}; - -#endif diff --git a/include/grains/SimplifiedGrainRepresentation.h b/include/grains/SimplifiedGrainRepresentation.h deleted file mode 100644 index c0b9edb60..000000000 --- a/include/grains/SimplifiedGrainRepresentation.h +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef INCLUDE_SIMPLIFIEDGRAINREPRESENTATION_H_ -#define INCLUDE_SIMPLIFIEDGRAINREPRESENTATION_H_ - -#include - -/** - * This class converts lists of grains and the vertices inside the grains to a - * simplified representation (currently spheres, other representations may be - * added later). Currently, assumptions are made that the elements are - * rectangular prisms. If not, a valid representation will still be made, but - * the centroid might be suboptimally placed. - */ -template -class SimplifiedGrainRepresentation -{ -public: - /** - * Constructor. Creates the simplified representation of a grain from its - * Grain. This sets all of the internal data members and is the only way - * to set the radius and center of the grain. The members order_parameter_id - * and old_order_parameter_id are initialized to the same value. - */ - SimplifiedGrainRepresentation(const Grain &grain); - - /** - * Getter for the grain center/centroid. - */ - [[nodiscard]] dealii::Point - getCenter() const; - - /** - * Getter for the grain radius. - */ - [[nodiscard]] double - getRadius() const; - - /** - * Getter for the grain id. - */ - [[nodiscard]] unsigned int - getGrainId() const; - - /** - * Setter for the grain id. - */ - void - setGrainId(unsigned int _grain_id); - - /** - * Getter for the order parameter id. - */ - [[nodiscard]] unsigned int - getOrderParameterId() const; - - /** - * Setter for the order parameter id. - */ - void - setOrderParameterId(unsigned int _order_parameter_id); - - /** - * Getter for the old value of the order parameter id (used in the - * transferGrainIds method of the SimplifiedGrainManipulator class). - */ - [[nodiscard]] unsigned int - getOldOrderParameterId() const; - - /** - * Setter for the distance from this grain to the nearest grain with the same - * order parameter. - */ - void - setDistanceToNeighbor(double dist); - - /** - * Getter for the distance from this grain to the nearest grain with the same - * order parameter - */ - [[nodiscard]] double - getDistanceToNeighbor() const; - -protected: - /** - * The center of the circle/sphere that represents the grain. - */ - dealii::Point center; - - /** - * The radius of the circle/sphere that represents the grain. - */ - double radius; - - /** - * The id of grain, which is used to distinguish between grains and give them - * different properties. - */ - unsigned int grain_id; - - /** - * The variable index of the order parameter containing this grain. - */ - unsigned int order_parameter_id; - - /** - * The variable index of the order parameter containing this grain before - * reassignment (which is performed outside this class). - */ - unsigned int old_order_parameter_id; - - /** - * The distance from this grain to the nearest grain with the same order - * parameter. This value is used to determine the cutoff distance for - * tranfering the grain between order parameters. - */ - double distance_to_neighbor_sharing_op; -}; - -/** - * This class contains methods to make changes to lists of - * SimplifiedGrainRepresentation objects to aid in order parameter remapping. - * Currently, this is a stub class containing two unrelated methods and no - * internal members. If this stays the case, this class may be absorbed into - * another one. - */ -template -class SimplifiedGrainManipulator -{ -public: - /** - * This method checks for collisions between SimplifiedGrainRepresentation - * objects with the same order parameter and reassigns them, if needed. - */ - void - reassignGrains(std::vector> &grain_representations, - double buffer_distance, - std::vector &order_parameter_id_list); - - /** - * This method checks the centers of two lists of - * SimplifiedGrainRepresentation objects from different times in the - * simulation and reassigns the grain ids so that they consistently refer to - * the same grains. - */ - void - transferGrainIds( - const std::vector> &old_grain_representations, - std::vector> &new_grain_representations) const; - -protected: -}; - -#endif diff --git a/include/grains/grains.h b/include/grains/grains.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/nucleation/nucleation.h b/include/nucleation/nucleation.h new file mode 100644 index 000000000..e69de29bb diff --git a/include/nucleation/nucleationParameters.h b/include/nucleation/nucleationParameters.h deleted file mode 100644 index fd6f418aa..000000000 --- a/include/nucleation/nucleationParameters.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef INCLUDE_NUCLEATIONPARAMETERS_H_ -#define INCLUDE_NUCLEATIONPARAMETERS_H_ - -#include - -#include -#include - -template -class nucleationParameters -{ -public: - unsigned int var_index; - std::vector semiaxes; - std::vector ellipsoid_rotation; - std::vector freeze_semiaxes; - double no_nucleation_border_thickness; - double hold_time; - dealii::Tensor<2, dim, double> rotation_matrix; - - nucleationParameters(unsigned int _var_index, - std::vector _semiaxes, - std::vector _freeze_semiaxes, - std::vector _ellipsoid_rotation, - double _hold_time, - double _no_nucleation_border_thickness) - : var_index(_var_index) - , semiaxes(std::move(_semiaxes)) - , ellipsoid_rotation(std::move(_ellipsoid_rotation)) - , freeze_semiaxes(std::move(_freeze_semiaxes)) - , no_nucleation_border_thickness(_no_nucleation_border_thickness) - , hold_time(_hold_time) - { - set_rotation_matrix(); - }; - - void - set_rotation_matrix() - { - // Rotation conventions: - // Rx refers to rotations about the x axis, Ry refers to rotations about the - // y axis, and Rz refers to rotations about the z axis. A positive rotation - // angle about the z axis corresponds to a clockwise rotation of the - // particle in a 2D calculation. - - double degrees_to_rad = std::acos(0.0) / 90.0; - - dealii::Tensor<2, dim, double> Rx; - dealii::Tensor<2, dim, double> Ry; - dealii::Tensor<2, dim, double> Rz; - - Rx[0][0] = 1.0; - Rx[1][1] = std::cos(ellipsoid_rotation.at(0) * degrees_to_rad); - - Ry[0][0] = std::cos(ellipsoid_rotation.at(1) * degrees_to_rad); - Ry[1][1] = 1.0; - - Rz[0][0] = std::cos(ellipsoid_rotation.at(2) * degrees_to_rad); - Rz[1][0] = std::sin(ellipsoid_rotation.at(2) * degrees_to_rad); - Rz[0][1] = -std::sin(ellipsoid_rotation.at(2) * degrees_to_rad); - Rz[1][1] = std::cos(ellipsoid_rotation.at(2) * degrees_to_rad); - - if (dim == 3) - { - Rx[1][2] = -std::sin(ellipsoid_rotation.at(0) * degrees_to_rad); - Rx[2][1] = std::sin(ellipsoid_rotation.at(0) * degrees_to_rad); - Rx[2][2] = std::cos(ellipsoid_rotation.at(0) * degrees_to_rad); - - Ry[0][2] = std::sin(ellipsoid_rotation.at(1) * degrees_to_rad); - Ry[2][0] = -std::sin(ellipsoid_rotation.at(1) * degrees_to_rad); - Ry[2][2] = std::cos(ellipsoid_rotation.at(1) * degrees_to_rad); - - Rz[2][2] = 1.0; - } - - rotation_matrix = Rx * Ry * Rz; // Note: these are tensor multiplications - }; -}; - -#endif diff --git a/include/nucleation/nucleus.h b/include/nucleation/nucleus.h deleted file mode 100644 index f32e6665f..000000000 --- a/include/nucleation/nucleus.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * nucleus.h - * - * Created on: Mar 6, 2017 - * Author: stephendewitt - */ - -#ifndef APPLICATIONS__NUCLEATIONMODEL_NUCLEUS_H_ -#define APPLICATIONS__NUCLEATIONMODEL_NUCLEUS_H_ - -#include - -#include - -// Structure representing each nucleus -template -struct nucleus -{ - unsigned int index; - dealii::Point center; - double radius; - std::vector semiaxes; - double seededTime, seedingTime; - unsigned int seedingTimestep; - unsigned int orderParameterIndex; -}; - -#endif /* APPLICATIONS__NUCLEATIONMODEL_NUCLEUS_H_ */ diff --git a/include/nucleation/parallelNucleationList.h b/include/nucleation/parallelNucleationList.h deleted file mode 100644 index f4720fa5d..000000000 --- a/include/nucleation/parallelNucleationList.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * parallelNucleationList.h - * - * Created on: Mar 10, 2017 - * Author: stephendewitt - */ - -#ifndef INCLUDE_PARALLELNUCLEATIONLIST_H_ -#define INCLUDE_PARALLELNUCLEATIONLIST_H_ - -#include - -template -class parallelNucleationList -{ -public: - explicit parallelNucleationList(std::vector> _newnuclei); - std::vector> - buildGlobalNucleiList(double min_dist_between_nuclei, unsigned int old_num_nuclei); - std::vector> - removeSubsetOfNuclei(std::vector nuclei_to_remove, - unsigned int nuclei_size); - -protected: - void - sendUpdate(int procno) const; - void - receiveUpdate(int procno); - void - broadcastUpdate(int broadcastProc, int thisProc); - void - resolveNucleationConflicts(double min_dist_between_nuclei, unsigned int old_num_nuclei); - std::vector> newnuclei; -}; - -#endif /* INCLUDE_PARALLELNUCLEATIONLIST_H_ */ diff --git a/include/utilities/computeStress.h b/include/utilities/computeStress.h deleted file mode 100644 index ae00cc082..000000000 --- a/include/utilities/computeStress.h +++ /dev/null @@ -1,389 +0,0 @@ -// implementation to compute stress given the elasticity matrix (CIJ) and strain -// tensor - -#ifndef COMPUTESTRESS_MECHANICS_H -#define COMPUTESTRESS_MECHANICS_H - -#include -#include - -// this source file is temporarily treated as a header file (hence -// #ifndef's) till library packaging scheme is finalized - -// #include "anisotropy.h" - -// Compute stress with displacement-gradient as input - -// Mathematical formulation (3D): S -> stress vector, R -> stress tensor, C -> -// stiffness tensor, E -> strain vector -// -// Generalized Hooke's law: -// [ S(0) ] [ C11 C12 C13 C14 C15 C16 ] [ E(0) ] -// [ S(1) ] [ C21 C22 C13 C14 C15 C16 ] [ E(1) ] -// [ S(2) ] [ C31 C32 C33 C34 C35 C36 ] [ E(2) ] -// [ S(3) ] = [ C41 C42 C43 C44 C45 C46 ] [ E(3) ] -// [ S(4) ] [ C51 C52 C53 C54 C55 C56 ] [ E(4) ] -// [ S(5) ] [ C61 C62 C63 C64 C65 C66 ] [ E(5) ] -// -// Strain vector definition: (using Ricci calculus notation, where commas in -// subscript refer to partial derivatives) E(0) = epsilon1 = epsilon11 = 1/2 ( -// u1,1 + u1,1 ) E(1) = epsilon2 = epsilon22 = 1/2 ( u2,2 + u2,2 ) E(2) = -// epsilon3 = epsilon33 = 1/2 ( u3,3 + u3,3 ) E(3) = epsilon4 = 2*epsilon23 = ( -// u2,3 + u3,2 ) E(4) = epsilon5 = 2*epsilon13 = ( u1,3 + u3,1 ) E(5) = epsilon6 -// = 2*epsilon12 = ( u1,2 + u2,1 ) -// -// Stress vector definition: -// S(0) = R[0][0] = sigma11 -// S(1) = R[1][1] = sigma22 -// S(2) = R[2][2] = sigma33 -// S(3) = R[1][2] = R[2][1] = sigma23 = sigma32 -// S(4) = R[0][2] = R[2][0] = sigma13 = sigma31 -// S(5) = R[0][1] = R[1][0] = sigma12 = sigma21 - -// Currently there are four overloaded versions of this function. They treat the -// cases where CIJ can be a table, a vectorized array, or a tensor and where -// strain and R can be vectorized arrays or tensors. Going forward, there may be -// a better way to reorganize this with templates. - -// Overloaded function where CIJ is a table, and the stress and strain are -// vectorized arrays -template -void -computeStress(const dealii::Table<2, double> &CIJ, - const dealii::VectorizedArray strain[][dim], - dealii::VectorizedArray R[][dim]) -{ - if (dim == 3) - { - dealii::VectorizedArray S[6], E[6]; - E[0] = strain[0][0]; - E[1] = strain[1][1]; - E[2] = strain[2][2]; - // In Voigt notation: Engineering shear strain=2*strain - E[3] = strain[1][2] + strain[2][1]; - E[4] = strain[0][2] + strain[2][0]; - E[5] = strain[0][1] + strain[1][0]; - for (unsigned int i = 0; i < 6; i++) - { - S[i] = 0.0; - for (unsigned int j = 0; j < 6; j++) - { - S[i] += CIJ(i, j) * E[j]; - } - } - R[0][0] = S[0]; - R[1][1] = S[1]; - R[2][2] = S[2]; - R[1][2] = S[3]; - R[0][2] = S[4]; - R[0][1] = S[5]; - R[2][1] = S[3]; - R[2][0] = S[4]; - R[1][0] = S[5]; - - // Optimized algorithm that skips the zero entries of CIJ for an - // orthotropic material and is a few percent faster - // dealii::VectorizedArray S[6], E[6]; - // E[0]=strain[0][0]; E[1]=strain[1][1]; E[2]=strain[2][2]; - // //In Voigt notation: Engineering shear strain=2*strain - // E[3]=strain[1][2]+strain[2][1]; - // E[4]=strain[0][2]+strain[2][0]; - // E[5]=strain[0][1]+strain[1][0]; - // for (unsigned int i=0; i<3; i++){ - // S[i]=0.0; - // for (unsigned int j=0; j<3; j++){ - // S[i]+=CIJ(i,j)*E[j]; - // } - // } - // - // for (unsigned int i=3; i<6; i++){ - // S[i]=CIJ(i,i)*E[i]; - // } - // R[0][0]=S[0]; R[1][1]=S[1]; R[2][2]=S[2]; - // R[1][2]=S[3]; R[0][2]=S[4]; R[0][1]=S[5]; - // R[2][1]=S[3]; R[2][0]=S[4]; R[1][0]=S[5]; - } - else if (dim == 2) - { - dealii::VectorizedArray S[3], E[3]; - E[0] = strain[0][0]; - E[1] = strain[1][1]; - // In Voigt notation: Engineering shear strain=2*strain - E[2] = strain[0][1] + strain[1][0]; - for (unsigned int i = 0; i < 3; i++) - { - S[i] = 0.0; - for (unsigned int j = 0; j < 3; j++) - { - S[i] += CIJ(i, j) * E[j]; - } - } - R[0][0] = S[0]; - R[1][1] = S[1]; - R[0][1] = S[2]; - R[1][0] = S[2]; - } - else - { - dealii::VectorizedArray S[1], E[1]; - E[0] = strain[0][0]; - S[0] = CIJ(0, 0) * E[0]; - R[0][0] = S[0]; - } -} - -// Overloaded function where CIJ, the strain, and the stress are all vectorized -// arrays -template -void -computeStress( - const dealii::VectorizedArray CIJ[2 * dim - 1 + dim / 3][2 * dim - 1 + dim / 3], - const dealii::VectorizedArray strain[][dim], - dealii::VectorizedArray R[][dim]) -{ - if (dim == 3) - { - dealii::VectorizedArray S[6], E[6]; - E[0] = strain[0][0]; - E[1] = strain[1][1]; - E[2] = strain[2][2]; - // In Voigt notation: Engineering shear strain=2*strain - E[3] = strain[1][2] + strain[2][1]; - E[4] = strain[0][2] + strain[2][0]; - E[5] = strain[0][1] + strain[1][0]; - for (unsigned int i = 0; i < 6; i++) - { - S[i] = 0.0; - for (unsigned int j = 0; j < 6; j++) - { - S[i] += CIJ[i][j] * E[j]; - } - } - R[0][0] = S[0]; - R[1][1] = S[1]; - R[2][2] = S[2]; - R[1][2] = S[3]; - R[0][2] = S[4]; - R[0][1] = S[5]; - R[2][1] = S[3]; - R[2][0] = S[4]; - R[1][0] = S[5]; - } - else if (dim == 2) - { - dealii::VectorizedArray S[3], E[3]; - E[0] = strain[0][0]; - E[1] = strain[1][1]; - // In Voigt notation: Engineering shear strain=2*strain - E[2] = strain[0][1] + strain[1][0]; - for (unsigned int i = 0; i < 3; i++) - { - S[i] = 0.0; - for (unsigned int j = 0; j < 3; j++) - { - S[i] += CIJ[i][j] * E[j]; - } - } - R[0][0] = S[0]; - R[1][1] = S[1]; - R[0][1] = S[2]; - R[1][0] = S[2]; - } - else - { - dealii::VectorizedArray S[1], E[1]; - E[0] = strain[0][0]; - S[0] = CIJ[0][0] * E[0]; - R[0][0] = S[0]; - } -} - -// Overloaded function where CIJ is stored as a tensor and the strain and stress -// are vectorized arrays -template -void -computeStress( - const dealii::Tensor<2, 2 * dim - 1 + dim / 3, dealii::VectorizedArray> &CIJ, - const dealii::VectorizedArray strain[][dim], - dealii::VectorizedArray R[][dim]) -{ - if (dim == 3) - { - dealii::VectorizedArray S[6], E[6]; - E[0] = strain[0][0]; - E[1] = strain[1][1]; - E[2] = strain[2][2]; - // In Voigt notation: Engineering shear strain=2*strain - E[3] = strain[1][2] + strain[2][1]; - E[4] = strain[0][2] + strain[2][0]; - E[5] = strain[0][1] + strain[1][0]; - for (unsigned int i = 0; i < 6; i++) - { - S[i] = 0.0; - for (unsigned int j = 0; j < 6; j++) - { - S[i] += CIJ[i][j] * E[j]; - } - } - R[0][0] = S[0]; - R[1][1] = S[1]; - R[2][2] = S[2]; - R[1][2] = S[3]; - R[0][2] = S[4]; - R[0][1] = S[5]; - R[2][1] = S[3]; - R[2][0] = S[4]; - R[1][0] = S[5]; - } - else if (dim == 2) - { - dealii::VectorizedArray S[3], E[3]; - E[0] = strain[0][0]; - E[1] = strain[1][1]; - // In Voigt notation: Engineering shear strain=2*strain - E[2] = strain[0][1] + strain[1][0]; - for (unsigned int i = 0; i < 3; i++) - { - S[i] = 0.0; - for (unsigned int j = 0; j < 3; j++) - { - S[i] += CIJ[i][j] * E[j]; - } - } - R[0][0] = S[0]; - R[1][1] = S[1]; - R[0][1] = S[2]; - R[1][0] = S[2]; - } - else - { - dealii::VectorizedArray S[1], E[1]; - E[0] = strain[0][0]; - S[0] = CIJ[0][0] * E[0]; - R[0][0] = S[0]; - } -} - -// Overloaded function where CIJ, the strain, and the stress are all stored as -// tensors -template -void -computeStress( - const dealii::Tensor<2, 2 * dim - 1 + dim / 3, dealii::VectorizedArray> &CIJ, - const dealii::Tensor<2, dim, dealii::VectorizedArray> strain, - dealii::Tensor<2, dim, dealii::VectorizedArray> &R) -{ - dealii::Tensor<1, 2 * dim - 1 + dim / 3, dealii::VectorizedArray> S, E; - - if (dim == 3) - { - E[0] = strain[0][0]; - E[1] = strain[1][1]; - E[2] = strain[2][2]; - // In Voigt notation: Engineering shear strain=2*strain - E[3] = strain[1][2] + strain[2][1]; - E[4] = strain[0][2] + strain[2][0]; - E[5] = strain[0][1] + strain[1][0]; - - // Multiply CIJ and E (in the language of Deal.II this is a tensor - // contraction) to get S - S = CIJ * E; - - R[0][0] = S[0]; - R[1][1] = S[1]; - R[2][2] = S[2]; - R[1][2] = S[3]; - R[0][2] = S[4]; - R[0][1] = S[5]; - R[2][1] = S[3]; - R[2][0] = S[4]; - R[1][0] = S[5]; - } - else if (dim == 2) - { - E[0] = strain[0][0]; - E[1] = strain[1][1]; - // In Voigt notation: Engineering shear strain=2*strain - E[2] = strain[0][1] + strain[1][0]; - - // Multiply CIJ and E (in the language of Deal.II this is a tensor - // contraction) to get S - S = CIJ * E; - - R[0][0] = S[0]; - R[1][1] = S[1]; - R[0][1] = S[2]; - R[1][0] = S[2]; - } - else - { - R[0][0] = CIJ[0][0] * strain[0][0]; - } -} - -// Overloaded function where CIJ is a table and the strain and the stress are -// stored as tensors -template -void -computeStress(const dealii::Table<2, double> &CIJ, - const dealii::Tensor<2, dim, dealii::VectorizedArray> &strain, - dealii::Tensor<2, dim, dealii::VectorizedArray> &R) -{ - if (dim == 3) - { - dealii::VectorizedArray S[6], E[6]; - E[0] = strain[0][0]; - E[1] = strain[1][1]; - E[2] = strain[2][2]; - // In Voigt notation: Engineering shear strain=2*strain - E[3] = strain[1][2] + strain[2][1]; - E[4] = strain[0][2] + strain[2][0]; - E[5] = strain[0][1] + strain[1][0]; - for (unsigned int i = 0; i < 6; i++) - { - S[i] = 0.0; - for (unsigned int j = 0; j < 6; j++) - { - S[i] += CIJ(i, j) * E[j]; - } - } - R[0][0] = S[0]; - R[1][1] = S[1]; - R[2][2] = S[2]; - R[1][2] = S[3]; - R[0][2] = S[4]; - R[0][1] = S[5]; - R[2][1] = S[3]; - R[2][0] = S[4]; - R[1][0] = S[5]; - } - else if (dim == 2) - { - dealii::VectorizedArray S[3], E[3]; - E[0] = strain[0][0]; - E[1] = strain[1][1]; - // In Voigt notation: Engineering shear strain=2*strain - E[2] = strain[0][1] + strain[1][0]; - for (unsigned int i = 0; i < 3; i++) - { - S[i] = 0.0; - for (unsigned int j = 0; j < 3; j++) - { - S[i] += CIJ[i][j] * E[j]; - } - } - R[0][0] = S[0]; - R[1][1] = S[1]; - R[0][1] = S[2]; - R[1][0] = S[2]; - } - else - { - dealii::VectorizedArray S[1], E[1]; - E[0] = strain[0][0]; - S[0] = CIJ[0][0] * E[0]; - R[0][0] = S[0]; - } -} - -#endif diff --git a/include/utilities/utilities.h b/include/utilities/utilities.h index 3f67f2a16..aa1ea279e 100644 --- a/include/utilities/utilities.h +++ b/include/utilities/utilities.h @@ -1,8 +1,9 @@ -#ifndef UTILITIES_H -#define UTILITIES_H +#ifndef utilities_h +#define utilities_h #include #include +#include #include #include @@ -26,7 +27,7 @@ make_tensor_of_vectorized_arrays(const std::vector &input_vector) dealii::Tensor<1, dim, dealii::VectorizedArray> tensor; // Populate the Tensor with vectorized arrays - for (unsigned int i = 0; i < input_vector.size(); ++i) + for (uint i = 0; i < input_vector.size(); ++i) { tensor[i] = dealii::make_vectorized_array(input_vector[i]); } @@ -34,4 +35,50 @@ make_tensor_of_vectorized_arrays(const std::vector &input_vector) return tensor; } +/** + * \brief Convert bool to string. + */ +inline const char * +bool_to_string(bool b) +{ + return b ? "true" : "false"; +} + +/** + * \brief Convert evaluation flags to string. + */ +inline std::string +eval_flags_to_string(dealii::EvaluationFlags::EvaluationFlags flag) +{ + std::string result; + + if (flag & dealii::EvaluationFlags::values) + { + result += "values"; + } + if (flag & dealii::EvaluationFlags::gradients) + { + if (!result.empty()) + { + result += " | "; + } + result += "gradients"; + } + if (flag & dealii::EvaluationFlags::hessians) + { + if (!result.empty()) + { + result += " | "; + } + result += "hessians"; + } + + if (result.empty()) + { + return "nothing"; + } + + return result; +} + #endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2c8d5b1b0..40513fa3f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,29 +1,10 @@ # Manually specify files to be included list(APPEND PRISMS_PF_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/boundary_conditions/boundaryConditions.cc - ${CMAKE_CURRENT_SOURCE_DIR}/boundary_conditions/markBoundaries.cc - ${CMAKE_CURRENT_SOURCE_DIR}/boundary_conditions/vectorBCFunction.cc - ${CMAKE_CURRENT_SOURCE_DIR}/initial_conditions/initialConditions.cc - ${CMAKE_CURRENT_SOURCE_DIR}/postprocessing/computeIntegral.cc - ${CMAKE_CURRENT_SOURCE_DIR}/postprocessing/postprocessor.cc - ${CMAKE_CURRENT_SOURCE_DIR}/refinement/AdaptiveRefinement.cc - ${CMAKE_CURRENT_SOURCE_DIR}/solvers/computeLHS.cc - ${CMAKE_CURRENT_SOURCE_DIR}/solvers/computeRHS.cc - ${CMAKE_CURRENT_SOURCE_DIR}/solvers/setNonlinearEqInitialGuess.cc - ${CMAKE_CURRENT_SOURCE_DIR}/solvers/solve.cc - ${CMAKE_CURRENT_SOURCE_DIR}/solvers/solveIncrement.cc - ${CMAKE_CURRENT_SOURCE_DIR}/solvers/SolverParameters.cc - ${CMAKE_CURRENT_SOURCE_DIR}/buildFields.cc - ${CMAKE_CURRENT_SOURCE_DIR}/checkpoint.cc - ${CMAKE_CURRENT_SOURCE_DIR}/init.cc - ${CMAKE_CURRENT_SOURCE_DIR}/inputFileReader.cc - ${CMAKE_CURRENT_SOURCE_DIR}/invM.cc - ${CMAKE_CURRENT_SOURCE_DIR}/matrixFreePDE.cc - ${CMAKE_CURRENT_SOURCE_DIR}/outputResults.cc - ${CMAKE_CURRENT_SOURCE_DIR}/reinit.cc - ${CMAKE_CURRENT_SOURCE_DIR}/userInputParameters.cc - ${CMAKE_CURRENT_SOURCE_DIR}/variableAttributeLoader.cc - ${CMAKE_CURRENT_SOURCE_DIR}/variableAttributes.cc - ${CMAKE_CURRENT_SOURCE_DIR}/variableContainer.cc + ${CMAKE_CURRENT_SOURCE_DIR}/user_inputs/input_file_reader.cc + ${CMAKE_CURRENT_SOURCE_DIR}/user_inputs/user_input_parameters.cc + ${CMAKE_CURRENT_SOURCE_DIR}/conditional_ostreams.cc + ${CMAKE_CURRENT_SOURCE_DIR}/variable_attribute_loader.cc + ${CMAKE_CURRENT_SOURCE_DIR}/variable_attributes.cc + ${CMAKE_CURRENT_SOURCE_DIR}/variable_container.cc ) set(PRISMS_PF_SOURCE_FILES ${PRISMS_PF_SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/core/boundary_conditions/boundaryConditions.cc b/src/core/boundary_conditions/boundaryConditions.cc deleted file mode 100644 index 90b55028d..000000000 --- a/src/core/boundary_conditions/boundaryConditions.cc +++ /dev/null @@ -1,332 +0,0 @@ -// methods to apply boundary conditons - -#include -#include -#include -#include - -// ================================================================================= -// Methods to apply non-zero Neumann BCs -// ================================================================================= -template -void -MatrixFreePDE::applyNeumannBCs() -{ - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // NOTE: Currently this function doesn't work and it's call is commented out - // in solveIncrement. The result is off by almost exactly a factor of 100,000. - // I don't know what the issue is. - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - // Check to the BC for the current field - unsigned int starting_BC_list_index = 0; - for (unsigned int i = 0; i < currentFieldIndex; i++) - { - if (var_attributes.at(i).var_type == SCALAR) - { - starting_BC_list_index++; - } - else - { - starting_BC_list_index += dim; - } - } - - if (var_attributes.at(currentFieldIndex).var_type == SCALAR) - { - for (unsigned int direction = 0; direction < 2 * dim; direction++) - { - if (userInputs.BC_list[starting_BC_list_index].var_BC_type[direction] == - NEUMANN) - { - FESystem *fe = FESet[currentFieldIndex]; - QGaussLobatto face_quadrature_formula(degree + 1); - FEFaceValues fe_face_values(*fe, - face_quadrature_formula, - update_values | update_JxW_values); - const unsigned int n_face_q_points = face_quadrature_formula.size(); - const unsigned int dofs_per_cell = fe->dofs_per_cell; - Vector cell_rhs(dofs_per_cell); - std::vector local_dof_indices(dofs_per_cell); - - // Loop over each face on a boundary - for (const auto &cell : dofHandlersSet[0]->active_cell_iterators()) - { - for (unsigned int f = 0; f < GeometryInfo::faces_per_cell; ++f) - { - if (cell->face(f)->at_boundary()) - { - if (cell->face(f)->boundary_id() == direction) - { - fe_face_values.reinit(cell, f); - cell_rhs = 0.0; - for (unsigned int q_point = 0; q_point < n_face_q_points; - ++q_point) - { - double neumann_value = - userInputs.BC_list[starting_BC_list_index] - .var_BC_val[direction]; - for (unsigned int i = 0; i < dofs_per_cell; ++i) - { - cell_rhs(i) += - (neumann_value * - fe_face_values.shape_value(i, q_point) * - fe_face_values.JxW(q_point)); - } - } - cell->get_dof_indices(local_dof_indices); - // assemble - for (unsigned int i = 0; i < dofs_per_cell; ++i) - { - (*(residualSet[currentFieldIndex])) - [local_dof_indices[i]] += cell_rhs(i); - } - } - } - } - } - } - } - } -} - -// ================================================================================= -// Methods to apply non-zero Dirichlet BCs -// ================================================================================= -template -void -MatrixFreePDE::applyDirichletBCs() -{ - // First, get the variable index of the current field - unsigned int starting_BC_list_index = 0; - - for (unsigned int i = 0; i < currentFieldIndex; i++) - { - if (var_attributes.at(i).var_type == SCALAR) - { - starting_BC_list_index++; - } - else - { - starting_BC_list_index += dim; - } - } - - if (var_attributes.at(currentFieldIndex).var_type == SCALAR) - { - for (unsigned int direction = 0; direction < 2 * dim; direction++) - { - if (userInputs.BC_list[starting_BC_list_index].var_BC_type[direction] == - DIRICHLET) - { - VectorTools::interpolate_boundary_values( - *dofHandlersSet[currentFieldIndex], - direction, - Functions::ConstantFunction( - userInputs.BC_list[starting_BC_list_index].var_BC_val[direction], - 1), - *(AffineConstraints *) - constraintsDirichletSet[currentFieldIndex]); - } - else if (userInputs.BC_list[starting_BC_list_index].var_BC_type[direction] == - NON_UNIFORM_DIRICHLET) - { - VectorTools::interpolate_boundary_values( - *dofHandlersSet[currentFieldIndex], - direction, - NonUniformDirichletBC(currentFieldIndex, - direction, - currentTime, - this), - *(AffineConstraints *) - constraintsDirichletSet[currentFieldIndex]); - } - } - } - else - { - for (unsigned int direction = 0; direction < 2 * dim; direction++) - { - std::vector BC_values; - for (unsigned int component = 0; component < dim; component++) - { - BC_values.push_back(userInputs.BC_list[starting_BC_list_index + component] - .var_BC_val[direction]); - } - - std::vector mask; - for (unsigned int component = 0; component < dim; component++) - { - if (userInputs.BC_list[starting_BC_list_index + component] - .var_BC_type[direction] == DIRICHLET) - { - mask.push_back(true); - } - else - { - mask.push_back(false); - } - } - - VectorTools::interpolate_boundary_values( - *dofHandlersSet[currentFieldIndex], - direction, - vectorBCFunction(BC_values), - *(AffineConstraints *) constraintsDirichletSet[currentFieldIndex], - mask); - - // Mask again, this time for non-uniform Dirichlet BCs - mask.clear(); - for (unsigned int component = 0; component < dim; component++) - { - if (userInputs.BC_list[starting_BC_list_index + component] - .var_BC_type[direction] == NON_UNIFORM_DIRICHLET) - { - mask.push_back(true); - } - else - { - mask.push_back(false); - } - } - - // VectorTools::interpolate_boundary_values - // (*dofHandlersSet[currentFieldIndex],direction, - // NonUniformDirichletBC(currentFieldIndex,direction,currentTime,this), - // *(AffineConstraints*)constraintsDirichletSet[currentFieldIndex],mask); - VectorTools::interpolate_boundary_values( - *dofHandlersSet[currentFieldIndex], - direction, - NonUniformDirichletBCVector(currentFieldIndex, - direction, - currentTime, - this), - *(AffineConstraints *) constraintsDirichletSet[currentFieldIndex], - mask); - } - } -} - -// Based on the contents of BC_list, mark faces on the triangulation as periodic -template -void -MatrixFreePDE::setPeriodicity() -{ - std::vector::cell_iterator>> - periodicity_vector; - for (int i = 0; i < dim; ++i) - { - bool periodic_pair = false; - for (unsigned int field_num = 0; field_num < userInputs.BC_list.size(); field_num++) - { - if (userInputs.BC_list[field_num].var_BC_type[2 * i] == PERIODIC) - { - periodic_pair = true; - } - } - if (periodic_pair) - { - GridTools::collect_periodic_faces(triangulation, - /*b_id1*/ 2 * i, - /*b_id2*/ 2 * i + 1, - /*direction*/ i, - periodicity_vector); - } - } - - triangulation.add_periodicity(periodicity_vector); - pcout << "periodic facepairs: " << periodicity_vector.size() << std::endl; -} - -// Set constraints to enforce periodic boundary conditions -template -void -MatrixFreePDE::setPeriodicityConstraints( - AffineConstraints *constraints, - const DoFHandler *dof_handler) const -{ - // First, get the variable index of the current field - unsigned int starting_BC_list_index = 0; - for (unsigned int i = 0; i < currentFieldIndex; i++) - { - if (var_attributes.at(i).var_type == SCALAR) - { - starting_BC_list_index++; - } - else - { - starting_BC_list_index += dim; - } - } - - std::vector::cell_iterator>> - periodicity_vector; - for (int i = 0; i < dim; ++i) - { - if (userInputs.BC_list[starting_BC_list_index].var_BC_type[2 * i] == PERIODIC) - { - GridTools::collect_periodic_faces(*dof_handler, - /*b_id1*/ 2 * i, - /*b_id2*/ 2 * i + 1, - /*direction*/ i, - periodicity_vector); - } - } - - DoFTools::make_periodicity_constraints(periodicity_vector, *constraints); -} - -template -void -MatrixFreePDE::set_rigid_body_mode_constraints( - AffineConstraints *constraints, - const DoFHandler *dof_handler, - const Point target_point) const -{ - // Determine the number of components in the field. For a scalar field this is 1, for a - // vector dim, etc. - unsigned int n_components = 0; - var_attributes.at(currentFieldIndex).var_type == VECTOR ? n_components = dim - : n_components = 1; - - // Loop over each locally owned cell - for (const auto &cell : dof_handler->active_cell_iterators()) - { - if (cell->is_locally_owned()) - { - for (unsigned int i = 0; i < GeometryInfo::vertices_per_cell; ++i) - { - // Check if the vertex is the target vertex - if (target_point.distance(cell->vertex(i)) < 1.0e-2 * cell->diameter()) - { - // Loop through the number of components and add the constraint - for (unsigned int component = 0; component < n_components; component++) - { - unsigned int nodeID = cell->vertex_dof_index(i, component); - constraints->add_line(nodeID); - constraints->set_inhomogeneity(nodeID, 0.0); - } - } - } - } - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/boundary_conditions/boundary_conditions.cc b/src/core/boundary_conditions/boundary_conditions.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/boundary_conditions/markBoundaries.cc b/src/core/boundary_conditions/markBoundaries.cc deleted file mode 100644 index 4723df5fa..000000000 --- a/src/core/boundary_conditions/markBoundaries.cc +++ /dev/null @@ -1,51 +0,0 @@ -// methods to mark boundaries - -#include -#include - -// methods to mark boundaries -// methods to mark boundaries -template -void -MatrixFreePDE::markBoundaries( - parallel::distributed::Triangulation &tria) const -{ - for (const auto &cell : tria.active_cell_iterators()) - { - // Mark all of the faces - for (unsigned int face_number = 0; face_number < GeometryInfo::faces_per_cell; - ++face_number) - { - for (unsigned int i = 0; i < dim; i++) - { - if (std::fabs(cell->face(face_number)->center()(i) - (0)) < 1e-12) - { - cell->face(face_number)->set_boundary_id(2 * i); - } - else if (std::fabs(cell->face(face_number)->center()(i) - - (userInputs.domain_size[i])) < 1e-12) - { - cell->face(face_number)->set_boundary_id(2 * i + 1); - } - } - } - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; diff --git a/src/core/boundary_conditions/vectorBCFunction.cc b/src/core/boundary_conditions/vectorBCFunction.cc deleted file mode 100644 index e7be3c9f8..000000000 --- a/src/core/boundary_conditions/vectorBCFunction.cc +++ /dev/null @@ -1,45 +0,0 @@ -/* - * vectorBCFunction.cc - * - * Created on: Feb 22, 2017 - * Author: stephendewitt - */ - -#include -#include - -#include - -template -vectorBCFunction::vectorBCFunction(const std::vector &BC_values) - : dealii::Function(dim) - , BC_values(BC_values) -{} - -template -void -vectorBCFunction::vector_value([[maybe_unused]] const dealii::Point &p, - dealii::Vector &values) const -{ - for (unsigned int i = 0; i < dim; i++) - { - values(i) = BC_values[i]; - } -} - -template -void -vectorBCFunction::vector_value_list( - const std::vector> &points, - std::vector> &value_list) const -{ - const unsigned int n_points = points.size(); - for (unsigned int p = 0; p < n_points; ++p) - { - vectorBCFunction::vector_value(points[p], value_list[p]); - } -} - -template class vectorBCFunction<1>; -template class vectorBCFunction<2>; -template class vectorBCFunction<3>; diff --git a/src/core/buildFields.cc b/src/core/buildFields.cc deleted file mode 100644 index 497b7aed5..000000000 --- a/src/core/buildFields.cc +++ /dev/null @@ -1,41 +0,0 @@ -/* - * buildFields.cc - * - * Created on: Feb 22, 2017 - * Author: stephendewitt - */ - -// ===================================================================== -// FUNCTION TO BUILD THE VECTOR OF FIELDS -// ===================================================================== - -#include - -template -void -MatrixFreePDE::buildFields() -{ - // Build each of the fields in the system - for (const auto &[index, variable] : var_attributes) - { - fields.push_back(Field(variable.var_type, variable.eq_type, variable.name)); - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/checkpoint.cc b/src/core/checkpoint.cc index 096b5b45b..e69de29bb 100644 --- a/src/core/checkpoint.cc +++ b/src/core/checkpoint.cc @@ -1,293 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -#ifdef DEAL_II_WITH_ZLIB -# include -#endif - -// Save a checkpoint -template -void -MatrixFreePDE::save_checkpoint() -{ - computing_timer.enter_subsection("matrixFreePDE: save_checkpoint"); - unsigned int my_id = Utilities::MPI::this_mpi_process(MPI_COMM_WORLD); - - if (my_id == 0) - { - // if we have previously written a snapshot, then keep the last - // snapshot in case this one fails to save. Note: static variables - // will only be initialized once per model run. - static bool previous_snapshot_exists = (userInputs.resume_from_checkpoint == true); - - if (previous_snapshot_exists) - { - move_file("restart.mesh", "restart.mesh.old"); - move_file("restart.mesh.info", "restart.mesh.info.old"); - move_file("restart.time.info", "restart.time.info.old"); - } - // from now on, we know that if we get into this - // function again that a snapshot has previously - // been written - previous_snapshot_exists = true; - } - - // save Triangulation and Solution vectors: - { - // Serializing all of the scalars together and all of the vectors together - - // First, get lists of scalar and vector fields - std::vector scalar_var_indices; - std::vector vector_var_indices; - for (const auto &[index, variable] : var_attributes) - { - if (variable.var_type == SCALAR) - { - scalar_var_indices.push_back(index); - } - else - { - vector_var_indices.push_back(index); - } - } - - // Second, build one solution set list for scalars and one for vectors - std::vector *> - solSet_transfer_scalars; - std::vector *> - solSet_transfer_vectors; - for (const auto &[index, variable] : var_attributes) - { - if (variable.var_type == SCALAR) - { - solSet_transfer_scalars.push_back(solutionSet[index]); - } - else - { - solSet_transfer_vectors.push_back(solutionSet[index]); - } - } - - // Finally, save the triangulation and the solutionTransfer objects - if (scalar_var_indices.size() > 0 && vector_var_indices.size() == 0) - { - parallel::distributed:: - SolutionTransfer> - system_trans_scalars(*dofHandlersSet[scalar_var_indices[0]]); - system_trans_scalars.prepare_for_serialization(solSet_transfer_scalars); - - triangulation.save("restart.mesh"); - } - else if (scalar_var_indices.size() == 0 && vector_var_indices.size() > 0) - { - parallel::distributed:: - SolutionTransfer> - system_trans_vectors(*dofHandlersSet[vector_var_indices[0]]); - system_trans_vectors.prepare_for_serialization(solSet_transfer_vectors); - - triangulation.save("restart.mesh"); - } - else - { - parallel::distributed:: - SolutionTransfer> - system_trans_scalars(*dofHandlersSet[scalar_var_indices[0]]); - system_trans_scalars.prepare_for_serialization(solSet_transfer_scalars); - - parallel::distributed:: - SolutionTransfer> - system_trans_vectors(*dofHandlersSet[vector_var_indices[0]]); - system_trans_vectors.prepare_for_serialization(solSet_transfer_vectors); - - triangulation.save("restart.mesh"); - } - } - - // Save information about the current increment and current time - if (my_id == 0) - { - std::ofstream time_info_file; - time_info_file.open("restart.time.info"); - time_info_file << currentIncrement << " (currentIncrement)\n"; - time_info_file << currentTime << " (currentTime)\n"; - time_info_file.close(); - } - - pcout << "*** Checkpoint created! ***\n\n"; - computing_timer.leave_subsection("matrixFreePDE: save_checkpoint"); -} - -// Load from a previously created checkpoint -template -void -MatrixFreePDE::load_checkpoint_triangulation() -{ - // First check existence of the two restart files for the mesh and field - // variables - verify_checkpoint_file_exists("restart.mesh"); - verify_checkpoint_file_exists("restart.mesh.info"); - - pcout << "\n*** Resuming from a checkpoint! ***\n\n"; - - try - { - triangulation.load("restart.mesh"); - } - catch (...) - { - AssertThrow(false, - ExcMessage("PRISMS-PF Error: Cannot open snapshot mesh file " - "or read the triangulation stored there.")); - } -} - -// Load from a previously saved checkpoint -template -void -MatrixFreePDE::load_checkpoint_fields() -{ - // Serializing all of the scalars together and all of the vectors together - - // First, get lists of scalar and vector fields - std::vector scalar_var_indices; - std::vector vector_var_indices; - for (const auto &[index, variable] : var_attributes) - { - if (variable.var_type == SCALAR) - { - scalar_var_indices.push_back(index); - } - else - { - vector_var_indices.push_back(index); - } - } - - // Second, build one solution set list for scalars and one for vectors - std::vector *> - solSet_transfer_scalars; - std::vector *> - solSet_transfer_vectors; - for (const auto &[index, variable] : var_attributes) - { - if (variable.var_type == SCALAR) - { - solSet_transfer_scalars.push_back(solutionSet[index]); - } - else - { - solSet_transfer_vectors.push_back(solutionSet[index]); - } - } - - // Finally, deserialize the fields to the solSet_transfer objects, which - // contain pointers to solutionSet - if (scalar_var_indices.size() > 0) - { - parallel::distributed:: - SolutionTransfer> - system_trans_scalars(*dofHandlersSet[scalar_var_indices[0]]); - system_trans_scalars.deserialize(solSet_transfer_scalars); - } - if (vector_var_indices.size() > 0) - { - parallel::distributed:: - SolutionTransfer> - system_trans_vectors(*dofHandlersSet[vector_var_indices[0]]); - system_trans_vectors.deserialize(solSet_transfer_vectors); - } -} - -// Load from a previously saved checkpoint -template -void -MatrixFreePDE::load_checkpoint_time_info() -{ - // Make sure that restart.time.info exists - verify_checkpoint_file_exists("restart.time.info"); - - std::ifstream time_info_file; - time_info_file.open("restart.time.info"); - std::string line; - std::getline(time_info_file, line); - line.erase(line.end() - 19, line.end()); - currentIncrement = dealii::Utilities::string_to_int(line); - - std::getline(time_info_file, line); - line.erase(line.end() - 14, line.end()); - currentTime = dealii::Utilities::string_to_double(line); - time_info_file.close(); -} - -// Move/rename a checkpoint file -template -void -MatrixFreePDE::move_file(const std::string &old_name, - const std::string &new_name) -{ - try - { - std::filesystem::rename(old_name, new_name); - } - catch (const std::filesystem::filesystem_error &error) - { - if (std::filesystem::exists(new_name)) - { - bool is_removed = std::filesystem::remove(new_name); - AssertThrow(is_removed, - dealii::ExcMessage( - "Unable to remove file: " + new_name + - ", although it seems to exist. The error code is " + - error.what())); - } - - try - { - std::filesystem::rename(old_name, new_name); - } - catch (const std::filesystem::filesystem_error &rename_error) - { - AssertThrow(false, - ExcMessage("Unable to rename file: " + old_name + " -> " + - new_name + ". Error: " + rename_error.what())); - } - } -} - -template -void -MatrixFreePDE::verify_checkpoint_file_exists(const std::string &filename) -{ - std::ifstream in(filename); - if (!in) - { - AssertThrow(false, - ExcMessage(std::string("PRISMS-PF Error: You are trying to " - "restart a previous computation, " - "but the restart file <") + - filename + "> does not appear to exist!")); - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/conditional_ostreams.cc b/src/core/conditional_ostreams.cc new file mode 100644 index 000000000..0018da71e --- /dev/null +++ b/src/core/conditional_ostreams.cc @@ -0,0 +1,49 @@ +#include + +// NOLINTBEGIN + +std::ofstream conditionalOStreams::summary_log_file("summary.log", + std::ios::out | std::ios::trunc); + +dealii::ConditionalOStream conditionalOStreams::pout_summary_instance( + summary_log_file, + dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0); + +const dealii::ConditionalOStream conditionalOStreams::pout_base( + std::cout, + dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0); + +const dealii::ConditionalOStream conditionalOStreams::pout_verbose( + std::cout, +#ifndef DEBUG + false && +#endif + dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0); + +conditionalOStreams::conditionalOStreams() +{ + if (!summary_log_file.is_open()) + { + throw std::runtime_error("Unable to open summary.log for writing."); + } +} + +conditionalOStreams::~conditionalOStreams() +{ + if (summary_log_file.is_open()) + { + summary_log_file.close(); + } +} + +dealii::ConditionalOStream & +conditionalOStreams::pout_summary() +{ + if (!summary_log_file.is_open()) + { + throw std::runtime_error("summary.log file is not open."); + } + return pout_summary_instance; +} + +// NOLINTEND \ No newline at end of file diff --git a/src/core/init.cc b/src/core/init.cc deleted file mode 100644 index f4fe66c8b..000000000 --- a/src/core/init.cc +++ /dev/null @@ -1,463 +0,0 @@ -// init() method for MatrixFreePDE class - -#include - -#include -#include - -// populate with fields and setup matrix free system -template -void -MatrixFreePDE::init() -{ - computing_timer.enter_subsection("matrixFreePDE: initialization"); - - // creating mesh - - pcout << "creating problem mesh...\n"; - // Create the coarse mesh and mark the boundaries - create_triangulation(triangulation); - - // Set which (if any) faces of the triangulation are periodic - setPeriodicity(); - - // If resuming from a checkpoint, load the refined triangulation, otherwise - // refine globally per the parameters.in file - if (userInputs.resume_from_checkpoint) - { - load_checkpoint_triangulation(); - } - else - { - // Do the initial global refinement - triangulation.refine_global(userInputs.refine_factor); - } - - // Write out the size of the computational domain and the total number of - // elements - if (dim < 3) - { - pcout << "problem dimensions: " << userInputs.domain_size[0] << "x" - << userInputs.domain_size[1] << "\n"; - } - else - { - pcout << "problem dimensions: " << userInputs.domain_size[0] << "x" - << userInputs.domain_size[1] << "x" << userInputs.domain_size[2] << "\n"; - } - pcout << "number of elements: " << triangulation.n_global_active_cells() << "\n\n"; - - // Setup system - pcout << "initializing matrix free object\n"; - totalDOFs = 0; - for (auto &field : fields) - { - currentFieldIndex = field.index; - - char buffer[100]; - - // print to std::out - std::string var_type; - if (field.pdetype == EXPLICIT_TIME_DEPENDENT) - { - var_type = "EXPLICIT_TIME_DEPENDENT"; - } - else if (field.pdetype == IMPLICIT_TIME_DEPENDENT) - { - var_type = "IMPLICIT_TIME_DEPENDENT"; - } - else if (field.pdetype == TIME_INDEPENDENT) - { - var_type = "TIME_INDEPENDENT"; - } - else if (field.pdetype == AUXILIARY) - { - var_type = "AUXILIARY"; - } - - snprintf(buffer, - sizeof(buffer), - "initializing finite element space P^%u for %9s:%6s field '%s'\n", - degree, - var_type.c_str(), - (field.type == SCALAR ? "SCALAR" : "VECTOR"), - field.name.c_str()); - pcout << buffer; - - // Check if any time dependent fields present - if (field.pdetype == EXPLICIT_TIME_DEPENDENT) - { - isTimeDependentBVP = true; - hasExplicitEquation = true; - } - else if (field.pdetype == IMPLICIT_TIME_DEPENDENT) - { - isTimeDependentBVP = true; - hasNonExplicitEquation = true; - std::cerr << "PRISMS-PF Error: IMPLICIT_TIME_DEPENDENT equation " - "types are not currently supported\n"; - abort(); - } - else if (field.pdetype == AUXILIARY) - { - hasNonExplicitEquation = true; - } - else if (field.pdetype == TIME_INDEPENDENT) - { - isEllipticBVP = true; - hasNonExplicitEquation = true; - } - - // create FESystem - FESystem *fe = nullptr; - - if (field.type == SCALAR) - { - fe = new FESystem(FE_Q(QGaussLobatto<1>(degree + 1)), 1); - } - else if (field.type == VECTOR) - { - fe = new FESystem(FE_Q(QGaussLobatto<1>(degree + 1)), dim); - } - else - { - pcout << "\nmatrixFreePDE.h: unknown field type\n"; - exit(-1); - } - FESet.push_back(fe); - - // distribute DOFs - DoFHandler *dof_handler = nullptr; - - dof_handler = new DoFHandler(triangulation); - dofHandlersSet.push_back(dof_handler); - dofHandlersSet_nonconst.push_back(dof_handler); - - dof_handler->distribute_dofs(*fe); - totalDOFs += dof_handler->n_dofs(); - - // Extract locally_relevant_dofs - IndexSet *locally_relevant_dofs = nullptr; - - locally_relevant_dofs = new IndexSet; - locally_relevant_dofsSet.push_back(locally_relevant_dofs); - locally_relevant_dofsSet_nonconst.push_back(locally_relevant_dofs); - - locally_relevant_dofs->clear(); - DoFTools::extract_locally_relevant_dofs(*dof_handler, *locally_relevant_dofs); - - // Create constraints - AffineConstraints *constraintsDirichlet = nullptr; - AffineConstraints *constraintsOther = nullptr; - - constraintsDirichlet = new AffineConstraints; - constraintsDirichletSet.push_back(constraintsDirichlet); - constraintsDirichletSet_nonconst.push_back(constraintsDirichlet); - constraintsOther = new AffineConstraints; - constraintsOtherSet.push_back(constraintsOther); - constraintsOtherSet_nonconst.push_back(constraintsOther); - valuesDirichletSet.push_back(new std::map); - - constraintsDirichlet->clear(); - constraintsDirichlet->reinit(*locally_relevant_dofs); - constraintsOther->clear(); - constraintsOther->reinit(*locally_relevant_dofs); - - // Get hanging node constraints - DoFTools::make_hanging_node_constraints(*dof_handler, *constraintsOther); - - // Pin solution - if (userInputs.pinned_point.find(currentFieldIndex) != - userInputs.pinned_point.end()) - { - set_rigid_body_mode_constraints(constraintsOther, - dof_handler, - userInputs.pinned_point[currentFieldIndex]); - } - - // Get constraints for periodic BCs - setPeriodicityConstraints(constraintsOther, dof_handler); - - // Check if Dirichlet BCs are used - for (unsigned int i = 0; i < userInputs.BC_list.size(); i++) - { - for (unsigned int direction = 0; direction < 2 * dim; direction++) - { - if (userInputs.BC_list[i].var_BC_type[direction] == DIRICHLET) - { - field.hasDirichletBCs = true; - } - else if (userInputs.BC_list[i].var_BC_type[direction] == - NON_UNIFORM_DIRICHLET) - { - field.hasnonuniformDirichletBCs = true; - } - else if (userInputs.BC_list[i].var_BC_type[direction] == NEUMANN) - { - field.hasNeumannBCs = true; - } - } - } - - // Get constraints for Dirichlet BCs - applyDirichletBCs(); - - constraintsDirichlet->close(); - constraintsOther->close(); - - // Store Dirichlet BC DOF's - valuesDirichletSet[field.index]->clear(); - for (types::global_dof_index i = 0; i < dof_handler->n_dofs(); i++) - { - if (locally_relevant_dofs->is_element(i)) - { - if (constraintsDirichlet->is_constrained(i)) - { - (*valuesDirichletSet[field.index])[i] = - constraintsDirichlet->get_inhomogeneity(i); - } - } - } - - snprintf(buffer, - sizeof(buffer), - "field '%2s' DOF : %u (Constraint DOF : %u)\n", - field.name.c_str(), - dof_handler->n_dofs(), - constraintsDirichlet->n_constraints()); - pcout << buffer; - } - pcout << "total DOF : " << totalDOFs << "\n"; - - // Setup the matrix free object - typename MatrixFree::AdditionalData additional_data; - additional_data.tasks_parallel_scheme = - MatrixFree::AdditionalData::partition_partition; - additional_data.mapping_update_flags = - (update_values | update_gradients | update_JxW_values | update_quadrature_points); - QGaussLobatto<1> quadrature(degree + 1); - matrixFreeObject.clear(); - matrixFreeObject.reinit(MappingFE(FE_Q(QGaussLobatto<1>(degree + 1))), - dofHandlersSet, - constraintsOtherSet, - quadrature, - additional_data); - - bool dU_scalar_init = false; - bool dU_vector_init = false; - - // Setup solution vectors - pcout << "initializing parallel::distributed residual and solution vectors\n"; - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - dealii::LinearAlgebra::distributed::Vector *U = nullptr; - dealii::LinearAlgebra::distributed::Vector *R = nullptr; - - U = new dealii::LinearAlgebra::distributed::Vector; - R = new dealii::LinearAlgebra::distributed::Vector; - solutionSet.push_back(U); - residualSet.push_back(R); - matrixFreeObject.initialize_dof_vector(*R, fieldIndex); - *R = 0; - - matrixFreeObject.initialize_dof_vector(*U, fieldIndex); - *U = 0; - - // Initializing temporary dU vector required for implicit solves of the - // elliptic equation. - if (fields[fieldIndex].pdetype == TIME_INDEPENDENT || - fields[fieldIndex].pdetype == IMPLICIT_TIME_DEPENDENT || - (fields[fieldIndex].pdetype == AUXILIARY && - var_attributes.at(fieldIndex).is_nonlinear)) - { - if (fields[fieldIndex].type == SCALAR) - { - if (!dU_scalar_init) - { - matrixFreeObject.initialize_dof_vector(dU_scalar, fieldIndex); - dU_scalar_init = true; - } - } - else - { - if (!dU_vector_init) - { - matrixFreeObject.initialize_dof_vector(dU_vector, fieldIndex); - dU_vector_init = true; - } - } - } - } - - // check if time dependent BVP and compute invM - if (isTimeDependentBVP) - { - computeInvM(); - } - - // Apply the initial conditions to the solution vectors - // The initial conditions are re-applied below in the "do_adaptive_refinement" - // function so that the mesh can adapt based on the initial conditions. - if (userInputs.resume_from_checkpoint) - { - load_checkpoint_fields(); - } - else - { - applyInitialConditions(); - } - - // Create new solution transfer sets (needed for the "refine_grid" call, might - // be able to move this elsewhere) - soltransSet.clear(); - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - soltransSet.push_back( - new parallel::distributed:: - SolutionTransfer>( - *dofHandlersSet_nonconst[fieldIndex])); - } - - // Ghost the solution vectors. Also apply the constraints (if any) on the - // solution vectors - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - constraintsDirichletSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - constraintsOtherSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - solutionSet[fieldIndex]->update_ghost_values(); - } - - // If not resuming from a checkpoint, check and perform adaptive mesh refinement, which - // reinitializes the system with the new mesh - if (!userInputs.resume_from_checkpoint && userInputs.h_adaptivity == true) - { - computing_timer.enter_subsection("matrixFreePDE: AMR"); - - unsigned int numDoF_preremesh = totalDOFs; - for (unsigned int remesh_index = 0; - remesh_index < - (userInputs.max_refinement_level - userInputs.min_refinement_level); - remesh_index++) - { - AMR.do_adaptive_refinement(currentIncrement); - reinit(); - if (totalDOFs == numDoF_preremesh) - { - break; - } - numDoF_preremesh = totalDOFs; - } - - computing_timer.leave_subsection("matrixFreePDE: AMR"); - } - - // If resuming from a checkpoint, load the proper starting increment and time - if (userInputs.resume_from_checkpoint) - { - load_checkpoint_time_info(); - } - - // Once the initial triangulation has been set, compute element volume - compute_element_volume(); - - computing_timer.leave_subsection("matrixFreePDE: initialization"); -} - -template -void -MatrixFreePDE::create_triangulation( - parallel::distributed::Triangulation &tria) const -{ - if (dim == 3) - { - GridGenerator::subdivided_hyper_rectangle(tria, - userInputs.subdivisions, - Point(), - Point(userInputs.domain_size[0], - userInputs.domain_size[1], - userInputs.domain_size[2])); - } - else if (dim == 2) - { - GridGenerator::subdivided_hyper_rectangle(tria, - userInputs.subdivisions, - Point(), - Point(userInputs.domain_size[0], - userInputs.domain_size[1])); - } - else - { - GridGenerator::subdivided_hyper_rectangle(tria, - userInputs.subdivisions, - Point(), - Point(userInputs.domain_size[0])); - } - - // Mark boundaries for applying the boundary conditions - markBoundaries(tria); -} - -template -void -MatrixFreePDE::compute_element_volume() -{ - // Get the number of cell batches. Note this is the same as the cell range in - // cell_loop() - const unsigned int n_cells = matrixFreeObject.n_cell_batches(); - - // Resize vector - element_volume.resize(n_cells); - - // Set quadrature rule and FEValues to update the JxW values - QGaussLobatto quadrature(degree + 1); - FEValues fe_values(*(FESet[0]), quadrature, update_JxW_values); - - // Get the number of quadrature points - const unsigned int num_quad_points = quadrature.size(); - - // Loop over the cells and each lane in the vectorized array - for (unsigned int cell = 0; cell < n_cells; cell++) - { - for (unsigned int lane = 0; - lane < matrixFreeObject.n_active_entries_per_cell_batch(cell); - lane++) - { - // Get the iterator for the current cell - auto cell_iterator = matrixFreeObject.get_cell_iterator(cell, lane); - - // Reinitialize the cell - fe_values.reinit(cell_iterator); - - // Initialize volume to 0 for the current cell - double cell_volume = 0.0; - - // Sum up the JxW values at each quadrature point to compute the element volume - // in 3D or area in 2D. - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - cell_volume += fe_values.JxW(q_point); - } - - // Store the element volume - element_volume[cell][lane] = cell_volume; - } - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; diff --git a/src/core/initial_conditions/initialConditions.cc b/src/core/initial_conditions/initialConditions.cc deleted file mode 100644 index 97dabc53c..000000000 --- a/src/core/initial_conditions/initialConditions.cc +++ /dev/null @@ -1,441 +0,0 @@ -#include -#include -#include -#include -#include -#include - -template -class InitialConditionPField : public Function -{ -public: - unsigned int index; - Vector values; - - using ScalarField = PRISMS::PField; - ScalarField &inputField; - - InitialConditionPField(const unsigned int _index, ScalarField &_inputField) - : Function(1) - , index(_index) - , inputField(_inputField) - {} - - [[nodiscard]] double - value(const Point &p, - [[maybe_unused]] const unsigned int component = 0) const override - { - double scalar_IC = NAN; - - double coord[dim]; - for (unsigned int i = 0; i < dim; i++) - { - coord[i] = p(i); - } - - scalar_IC = inputField(coord); - - return scalar_IC; - } -}; - -// methods to apply initial conditions -template -void -MatrixFreePDE::applyInitialConditions() -{ - if (userInputs.load_grain_structure) - { - // Create the dummy field - dealii::LinearAlgebra::distributed::Vector grain_index_field; - - // Clear the order parameter fields - unsigned int op_list_index = 0; - for (unsigned int var_index = 0; var_index < var_attributes.size(); var_index++) - { - if (op_list_index < userInputs.variables_for_remapping.size()) - { - if (var_index == userInputs.variables_for_remapping.at(op_list_index)) - { - *solutionSet[var_index] = 0.0; - op_list_index++; - } - } - } - - simplified_grain_representations.clear(); - - // Get the index of one of the scalar fields - unsigned int scalar_field_index = 0; - for (const auto &[index, variable] : var_attributes) - { - if (variable.var_type == SCALAR) - { - scalar_field_index = index; - break; - } - } - - matrixFreeObject.initialize_dof_vector(grain_index_field, scalar_field_index); - - // Declare the PField types and containers - typedef PRISMS::PField ScalarField; - typedef PRISMS::Body Body; - Body body; - - // Create the filename of the the file to be loaded - - // Load the data from the file using a PField - std::string filename = userInputs.grain_structure_filename; - filename += ".vtk"; - - // new section added for the choice of unstructured mesh and rectilinear mesh - if (userInputs.load_vtk_file_type == "UNSTRUCTURED") - { - body.read_vtk(filename); - } - else if (userInputs.load_vtk_file_type == "RECTILINEAR") - { - body.read_RL_vtk(filename); - } - else - { - pcout << "Error in vtk file type: Use either UNSTRUCTURED OR RECTILINEAR\n"; - abort(); - } // new section ends - - ScalarField &id_field = - body.find_scalar_field(userInputs.grain_structure_variable_name); - - pcout << "Applying PField initial condition...\n"; - - VectorTools::interpolate(*dofHandlersSet[scalar_field_index], - InitialConditionPField(0, id_field), - grain_index_field); - - grain_index_field.update_ghost_values(); - - // Get the max and min grain ids - auto max_id = (unsigned int) grain_index_field.linfty_norm(); - unsigned int min_id = 0; - - // Now locate all of the grains and create simplified representations of - // them - QGaussLobatto quadrature2(degree + 1); - FloodFiller flood_filler(*FESet.at(scalar_field_index), quadrature2); - - pcout << "Locating the grains...\n"; - std::vector> grain_sets; - for (unsigned int id = min_id; id < max_id + 1; id++) - { - pcout << "Locating grain " << id << "...\n"; - - std::vector> grain_sets_single_id; - - flood_filler.calcGrainSets(*FESet.at(scalar_field_index), - *dofHandlersSet_nonconst.at(scalar_field_index), - &grain_index_field, - (double) id - userInputs.order_parameter_threshold, - (double) id + userInputs.order_parameter_threshold, - min_id, - 0, - grain_sets_single_id); - - for (unsigned int g = 0; g < grain_sets_single_id.size(); g++) - { - grain_sets_single_id.at(g).setGrainIndex(id); - } - - grain_sets.insert(grain_sets.end(), - grain_sets_single_id.begin(), - grain_sets_single_id.end()); - } - - pcout << "Generating simplified representations of the grains...\n"; - for (unsigned int g = 0; g < grain_sets.size(); g++) - { - SimplifiedGrainRepresentation simplified_grain_representation( - grain_sets.at(g)); - - if (dim == 2) - { - pcout << "Grain: " << simplified_grain_representation.getGrainId() << " " - << simplified_grain_representation.getOrderParameterId() - << " Center: " << simplified_grain_representation.getCenter()(0) - << " " << simplified_grain_representation.getCenter()(1) - << " Radius: " << simplified_grain_representation.getRadius() - << std::endl; - } - else - { - pcout << "Grain: " << simplified_grain_representation.getGrainId() << " " - << simplified_grain_representation.getOrderParameterId() - << " Center: " << simplified_grain_representation.getCenter()(0) - << " " << simplified_grain_representation.getCenter()(1) << " " - << simplified_grain_representation.getCenter()(2) - << " Radius: " << simplified_grain_representation.getRadius() - << std::endl; - } - - simplified_grain_representations.push_back(simplified_grain_representation); - } - - // Delete grains with very small radii that correspond to a single - // interfacial element - for (unsigned int g = 0; g < simplified_grain_representations.size(); g++) - { - if (simplified_grain_representations.at(g).getRadius() < - userInputs.min_radius_for_loading_grains) - { - simplified_grain_representations.erase( - simplified_grain_representations.begin() + g); - g--; - } - } - - pcout << "Reassigning the grains to new order parameters...\n"; - SimplifiedGrainManipulator simplified_grain_manipulator; - simplified_grain_manipulator.reassignGrains(simplified_grain_representations, - userInputs.buffer_between_grains, - userInputs.variables_for_remapping); - - pcout << "After reassignment: " << std::endl; - for (unsigned int g = 0; g < simplified_grain_representations.size(); g++) - { - if (dim == 2) - { - pcout << "Grain: " << simplified_grain_representations.at(g).getGrainId() - << " " << simplified_grain_representations.at(g).getOrderParameterId() - << " Center: " - << simplified_grain_representations.at(g).getCenter()(0) << " " - << simplified_grain_representations.at(g).getCenter()(1) << std::endl; - } - else - { - pcout << "Grain: " << simplified_grain_representations.at(g).getGrainId() - << " " << simplified_grain_representations.at(g).getOrderParameterId() - << " Center: " - << simplified_grain_representations.at(g).getCenter()(0) << " " - << simplified_grain_representations.at(g).getCenter()(1) << " " - << simplified_grain_representations.at(g).getCenter()(2) << std::endl; - } - } - - pcout << "Placing the grains in their new order parameters...\n"; - OrderParameterRemapper order_parameter_remapper; - order_parameter_remapper.remap_from_index_field( - simplified_grain_representations, - &grain_index_field, - solutionSet, - *dofHandlersSet_nonconst.at(scalar_field_index), - FESet.at(scalar_field_index)->dofs_per_cell); - - // Smooth the order parameters according to Fick's 2nd Law - // In the time cycle below, we evolve the weak form of Eq.: - // field_i^(n+1)=field_i^(n)+D*dt*Laplacian(field_i^(n)) Old - // dt_for_smoothing double dt_for_smoothing = - // dealii::GridTools::minimal_cell_diameter(triangulation)/1000.0; NEW - // selection of dt_for_smoothing based on dt_max=(dx^2)/2*dim*D, where - // D=1, addording to the von Neumann stability condition We chose - // dt_for_smoothing=0.25*dt_max - double min_dx = dealii::GridTools::minimal_cell_diameter(triangulation) / - (1.0 * degree * std::sqrt(1.0 * dim)); - double dt_for_smoothing = 0.25 * min_dx * min_dx / (1.0 * dim); - - op_list_index = 0; - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - if (op_list_index < userInputs.variables_for_remapping.size()) - { - if (fieldIndex == userInputs.variables_for_remapping.at(op_list_index)) - { - for (unsigned int cycle = 0; - cycle < userInputs.num_grain_smoothing_cycles; - cycle++) - { - // Calculates the Laplace RHS and stores the information - // in residualSet - computeLaplaceRHS(fieldIndex); - if (fields[fieldIndex].type == SCALAR) - { - unsigned int invM_size = invMscalar.locally_owned_size(); - for (unsigned int dof = 0; - dof < solutionSet[fieldIndex]->locally_owned_size(); - ++dof) - { - solutionSet[fieldIndex]->local_element(dof) = - solutionSet[fieldIndex]->local_element(dof) - - invMscalar.local_element(dof % invM_size) * - residualSet[fieldIndex]->local_element(dof) * - dt_for_smoothing; - } - } - else if (fields[fieldIndex].type == VECTOR) - { - unsigned int invM_size = invMvector.locally_owned_size(); - for (unsigned int dof = 0; - dof < solutionSet[fieldIndex]->locally_owned_size(); - ++dof) - { - solutionSet[fieldIndex]->local_element(dof) = - solutionSet[fieldIndex]->local_element(dof) - - invMvector.local_element(dof % invM_size) * - residualSet[fieldIndex]->local_element(dof) * - dt_for_smoothing; - } - } - - solutionSet[fieldIndex]->update_ghost_values(); - } - - op_list_index++; - } - } - } - } - - // Create map of vtk files that are read in - std::unordered_map> file_field_map; - for (size_t i = 0; i < var_attributes.size(); ++i) - { - if (userInputs.load_ICs[i]) - { - file_field_map[userInputs.load_file_name[i]].push_back(i); - } - } - // Read out unique filenames - if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0 && !file_field_map.empty()) - { - std::cout << "Unique VTK input files: " << std::endl; - for (const auto &pair : file_field_map) - { - std::cout << pair.first << ", "; - } - } - // Read in each vtk once and apply initial conditions - using ScalarField = PRISMS::PField; - using Body = PRISMS::Body; - Body body; - - for (const auto &pair : file_field_map) - { - bool using_parallel_files = false; - std::string filename = pair.first; - std::vector index_list = pair.second; - // For parallel file capability - for (const auto &index : index_list) - { - using_parallel_files = - using_parallel_files || userInputs.load_parallel_file[index]; - } - if (using_parallel_files) - { - std::ostringstream conversion; - conversion << Utilities::MPI::this_mpi_process(MPI_COMM_WORLD); - filename.append("." + conversion.str() + ".vtk"); - } - else - { - filename.append(".vtk"); - } - - std::cout << "Reading " << filename << "\n"; - // Load the data from the file using a PField - // new section added for the choice of unstructured mesh and rectilinear mesh - if (userInputs.load_vtk_file_type == "UNSTRUCTURED") - { - body.read_vtk(filename); - } - else if (userInputs.load_vtk_file_type == "RECTILINEAR") - { - body.read_RL_vtk(filename); - } - else - { - pcout << "Error in vtk file type: Use either UNSTRUCTURED OR RECTILINEAR\n"; - abort(); - } // new section ends - - for (const auto &index : index_list) - { - std::string var_name = userInputs.load_field_name[index]; - - // Find the scalar field in the file - ScalarField &field = body.find_scalar_field(var_name); - - if (var_attributes.at(index).var_type == SCALAR) - { - pcout << "Applying PField initial condition for " - << userInputs.load_field_name[index] << "...\n"; - VectorTools::interpolate(*dofHandlersSet[index], - InitialConditionPField(index, field), - *solutionSet[index]); - } - else - { - AssertThrow(false, - ExcMessage("PRISMS-PF Error: We do not support the loading of " - "vector fields from vtks at this moment.")); - } - } - } - - unsigned int op_list_index = 0; - for (const auto &[var_index, variable] : var_attributes) - { - bool is_remapped_op = false; - if (op_list_index < userInputs.variables_for_remapping.size()) - { - if (var_index == userInputs.variables_for_remapping.at(op_list_index)) - { - is_remapped_op = true; - op_list_index++; - } - } - - if (!is_remapped_op || !userInputs.load_grain_structure) - { - if (userInputs.load_ICs[var_index] == false) - { - pcout << "Applying non-PField initial condition...\n"; - - if (variable.var_type == SCALAR) - { - VectorTools::interpolate(*dofHandlersSet[var_index], - InitialCondition(var_index, - userInputs, - this), - *solutionSet[var_index]); - } - else if (variable.var_type == VECTOR) - { - VectorTools::interpolate(*dofHandlersSet[var_index], - InitialConditionVector(var_index, - userInputs, - this), - *solutionSet[var_index]); - } - } - pcout << "Application of initial conditions for field number " << var_index - << " complete \n"; - } - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/initial_conditions/initial_conditions.cc b/src/core/initial_conditions/initial_conditions.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/invM.cc b/src/core/invM.cc deleted file mode 100644 index b98f0ae4c..000000000 --- a/src/core/invM.cc +++ /dev/null @@ -1,163 +0,0 @@ -// computeInvM() method for MatrixFreePDE class -#include - -#include - -// compute inverse of the diagonal mass matrix and store in vector invM -template -void -MatrixFreePDE::computeInvM() -{ - // Find invM indices by looping through all fields for the first field of that - // type - //(e.g., the index of the first explicit time dependent scalar field) - // This has to be done for all types of fields. - - // This only has to be done when the fields are first initialized. It's - // redundant for any reinitialized like in adaptive mesh refinement (AMR). - // Hopefully the performance is fine - bool invMscalarFound = false; - bool invMvectorFound = false; - - unsigned int parabolicScalarFieldIndex = 0; - unsigned int parabolicVectorFieldIndex = 0; - - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - if (fields[fieldIndex].pdetype == EXPLICIT_TIME_DEPENDENT || - fields[fieldIndex].pdetype == AUXILIARY) - { - if (fields[fieldIndex].type == SCALAR && !invMscalarFound) - { - parabolicScalarFieldIndex = fieldIndex; - invMscalarFound = true; - continue; - } - else if (fields[fieldIndex].type == VECTOR && !invMvectorFound) - { - parabolicVectorFieldIndex = fieldIndex; - invMvectorFound = true; - continue; - } - } - } - - // Initialize invM and clear its values - matrixFreeObject.initialize_dof_vector(invMscalar, parabolicScalarFieldIndex); - invMscalar = 0.0; - matrixFreeObject.initialize_dof_vector(invMvector, parabolicVectorFieldIndex); - invMvector = 0.0; - - // invM evaluation flags - dealii::EvaluationFlags::EvaluationFlags invM_flags = dealii::EvaluationFlags::values; - - // Compute mass matrix for the given type of quadrature. Selecting gauss - // lobatto quadrature points which are suboptimal but give diagonal M - if (fields[parabolicScalarFieldIndex].type == SCALAR) - { - VectorizedArray one = make_vectorized_array(1.0); - FEEvaluation fe_eval(matrixFreeObject, parabolicScalarFieldIndex); - - const unsigned int n_q_points = fe_eval.n_q_points; - for (unsigned int cell = 0; cell < matrixFreeObject.n_cell_batches(); ++cell) - { - fe_eval.reinit(cell); - for (unsigned int q = 0; q < n_q_points; ++q) - { - fe_eval.submit_value(one, q); - } - fe_eval.integrate(invM_flags); - fe_eval.distribute_local_to_global(invMscalar); - } - } - if (fields[parabolicVectorFieldIndex].type == VECTOR) - { - dealii::Tensor<1, dim, dealii::VectorizedArray> oneV; - for (unsigned int i = 0; i < dim; i++) - { - oneV[i] = 1.0; - } - - FEEvaluation fe_eval(matrixFreeObject, - parabolicVectorFieldIndex); - - const unsigned int n_q_points = fe_eval.n_q_points; - for (unsigned int cell = 0; cell < matrixFreeObject.n_cell_batches(); ++cell) - { - fe_eval.reinit(cell); - for (unsigned int q = 0; q < n_q_points; ++q) - { - fe_eval.submit_value(oneV, q); - } - fe_eval.integrate(invM_flags); - fe_eval.distribute_local_to_global(invMvector); - } - } - - invMscalar.compress(VectorOperation::add); - invMvector.compress(VectorOperation::add); - - // Calculate the volume of the smallest cell to prevent a non-zero value of - // invM being confused for a near zero value (which can happen if the domain - // size is 1e-6 or below) - std::vector min_element_length; - for (unsigned int d = 0; d < dim; d++) - { - int num_elements = - userInputs.subdivisions.at(d) * - dealii::Utilities::fixed_power<2>(userInputs.max_refinement_level); - min_element_length.push_back(userInputs.domain_size[d] / double(num_elements)); - } - double min_cell_volume = std::accumulate(begin(min_element_length), - end(min_element_length), - 1.0, - std::multiplies<>()); - - // Invert scalar mass matrix diagonal elements - for (unsigned int k = 0; k < invMscalar.locally_owned_size(); ++k) - { - if (std::abs(invMscalar.local_element(k)) > 1.0e-15 * min_cell_volume) - { - invMscalar.local_element(k) = 1. / invMscalar.local_element(k); - } - else - { - invMscalar.local_element(k) = 0; - } - } - pcout << "computed scalar mass matrix (using FE space for field: " - << parabolicScalarFieldIndex << ")\n"; - - // Invert vector mass matrix diagonal elements - for (unsigned int k = 0; k < invMvector.locally_owned_size(); ++k) - { - if (std::abs(invMvector.local_element(k)) > 1.0e-15 * min_cell_volume) - { - invMvector.local_element(k) = 1. / invMvector.local_element(k); - } - else - { - invMvector.local_element(k) = 0; - } - } - pcout << "computed vector mass matrix (using FE space for field: " - << parabolicVectorFieldIndex << ")\n"; -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/matrixFreePDE.cc b/src/core/matrixFreePDE.cc deleted file mode 100644 index c73e7cbe6..000000000 --- a/src/core/matrixFreePDE.cc +++ /dev/null @@ -1,92 +0,0 @@ -// constructor and destructor for matrixFreePDE class - -#include - -// constructor -template -MatrixFreePDE::MatrixFreePDE(userInputParameters _userInputs) - : Subscriptor() - , pcout(std::cout, Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - , userInputs(_userInputs) - , var_attributes(_userInputs.var_attributes) - , pp_attributes(_userInputs.pp_attributes) - , triangulation(MPI_COMM_WORLD) - , currentFieldIndex(0) - , isTimeDependentBVP(false) - , isEllipticBVP(false) - , hasExplicitEquation(false) - , hasNonExplicitEquation(false) - , currentTime(0.0) - , currentIncrement(0) - , currentOutput(0) - , currentCheckpoint(0) - , current_grain_reassignment(0) - , computing_timer(pcout, TimerOutput::summary, TimerOutput::wall_times) - , first_integrated_var_output_complete(false) - , AMR(_userInputs, - triangulation, - fields, - solutionSet, - soltransSet, - FESet, - dofHandlersSet_nonconst, - constraintsDirichletSet, - constraintsOtherSet) -{} - -// destructor -template -MatrixFreePDE::~MatrixFreePDE() -{ - matrixFreeObject.clear(); - - // Delete the pointers contained in several member variable vectors - // The size of each of these must be checked individually in case an exception - // is thrown as they are being initialized. - for (const auto &locally_relevant_dofs : locally_relevant_dofsSet) - { - delete locally_relevant_dofs; - } - for (const auto &constraintsDirichlet : constraintsDirichletSet) - { - delete constraintsDirichlet; - } - for (const auto &soltrans : soltransSet) - { - delete soltrans; - } - for (const auto &dofHandlers : dofHandlersSet) - { - delete dofHandlers; - } - for (const auto &FE : FESet) - { - delete FE; - } - for (const auto &solution : solutionSet) - { - delete solution; - } - for (const auto &residual : residualSet) - { - delete residual; - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/outputResults.cc b/src/core/outputResults.cc deleted file mode 100644 index 68d1d9ed0..000000000 --- a/src/core/outputResults.cc +++ /dev/null @@ -1,241 +0,0 @@ -#include - -#include -#include -#include -#include - -template -void -MatrixFreePDE::outputResults() -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: output"); - - // create DataOut object - DataOut data_out; - - // loop over fields - - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - // mark field as scalar/vector - std::vector dataType( - fields[fieldIndex].numComponents, - (fields[fieldIndex].type == SCALAR - ? DataComponentInterpretation::component_is_scalar - : DataComponentInterpretation::component_is_part_of_vector)); - // add field to data_out - std::vector solutionNames(fields[fieldIndex].numComponents, - fields[fieldIndex].name.c_str()); - data_out.add_data_vector(*dofHandlersSet[fieldIndex], - *solutionSet[fieldIndex], - solutionNames, - dataType); - } - - // Test section for outputting postprocessed fields - // Currently there are hacks in place, using the matrixFreeObject, invM, - // constraints, and DoFHandler as the primary variables Currently only works - // for scalar fields. Require separate loop for vector-type post process - // fields - if (userInputs.postProcessingRequired) - { - std::vector *> postProcessedSet; - computePostProcessedFields(postProcessedSet); - unsigned int invM_size = invMscalar.locally_owned_size(); - for (auto &field : postProcessedSet) - { - for (unsigned int dof = 0; dof < field->locally_owned_size(); ++dof) - { - field->local_element(dof) = - invMscalar.local_element(dof % invM_size) * field->local_element(dof); - } - constraintsOtherSet[0]->distribute(*field); - field->update_ghost_values(); - } - - // Integrate over selected post-processed fields and output them to the - // screen and a text file - std::ofstream output_file; - - if (userInputs.num_integrated_fields > 0) - { - if (first_integrated_var_output_complete) - { - output_file.open("integratedFields.txt", std::ios::app); - } - else - { - output_file.open("integratedFields.txt", std::ios::out); - } - output_file.precision(10); - - if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - output_file << currentTime; - } - - for (const auto &[pp_index, pp_variable] : pp_attributes) - { - if (pp_variable.calc_integral) - { - double integrated_field = NAN; - computeIntegral(integrated_field, pp_index, postProcessedSet); - pcout << "Integrated value of " << pp_variable.name << ": " - << integrated_field << std::endl; - if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - output_file << "\t" << pp_variable.name << "\t" << integrated_field; - } - } - } - if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - output_file << "\n"; - } - output_file.close(); - first_integrated_var_output_complete = true; - } - - // Add the postprocessed fields to data_out - for (const auto &[fieldIndex, pp_variable] : pp_attributes) - { - // mark field as scalar/vector - unsigned int components = 0; - if (userInputs.pp_varInfoList[fieldIndex].is_scalar) - { - components = 1; - std::vector - dataType(components, DataComponentInterpretation::component_is_scalar); - std::vector solutionNames(components, pp_variable.name); - // add field to data_out - data_out.add_data_vector(*dofHandlersSet[0], - *postProcessedSet[fieldIndex], - solutionNames, - dataType); - } - else - { - components = dim; - std::vector - dataType(components, - DataComponentInterpretation::component_is_part_of_vector); - std::vector solutionNames(components, pp_variable.name); - // add field to data_out - data_out.add_data_vector(*dofHandlersSet[0], - *postProcessedSet[fieldIndex], - solutionNames, - dataType); - } - } - } - - data_out.build_patches(degree); - - // write to results file - // file name - std::ostringstream cycleAsString; - cycleAsString << std::setw(std::floor(std::log10(userInputs.totalIncrements)) + 1) - << std::setfill('0') << currentIncrement; - - std::ostringstream baseFileNameStream; - baseFileNameStream << userInputs.output_file_name << "-" << cycleAsString.str(); - std::string baseFileName = baseFileNameStream.str(); - - std::ostringstream vtuFileNameStream; - vtuFileNameStream << baseFileName << "." - << Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) << "." - << userInputs.output_file_type; - std::string vtuFileName = vtuFileNameStream.str(); - - // Write to file in either vtu or vtk format - if (userInputs.output_file_type == "vtu") - { - // Set flags to output the time and cycle number as part of the vtu file - dealii::DataOutBase::VtkFlags flags; - flags.time = currentTime; - flags.cycle = currentIncrement; - flags.print_date_and_time = true; - data_out.set_flags(flags); - - if (userInputs.output_vtu_per_process) - { - // Write the results to separate files for each process - std::ofstream output(vtuFileName); - data_out.write_vtu(output); - - // Create pvtu record that can be used to stitch together the results - // from all the processes - if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - std::vector filenames; - for (unsigned int i = 0; - i < Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD); - ++i) - { - std::ostringstream vtuProcFileNameStream; - vtuProcFileNameStream << userInputs.output_file_name << "-" - << cycleAsString.str() << "." << i << "." - << "userInputs.output_file_type"; - std::string vtuProcFileName = vtuProcFileNameStream.str(); - - filenames.emplace_back(vtuProcFileName); - } - - std::ostringstream pvtuFileNameStream; - pvtuFileNameStream << baseFileName << ".p" << userInputs.output_file_type; - std::string pvtuFileName = pvtuFileNameStream.str(); - - std::ofstream master_output(pvtuFileName); - - data_out.write_pvtu_record(master_output, filenames); - pcout << "Output written to:" << pvtuFileName << "\n\n"; - } - } - else - { - // Write the results to a file shared between all processes - std::ostringstream svtuFileNameStream; - svtuFileNameStream << baseFileName << "." << userInputs.output_file_type; - std::string svtuFileName = svtuFileNameStream.str(); - - data_out.write_vtu_in_parallel(svtuFileName, MPI_COMM_WORLD); - pcout << "Output written to:" << svtuFileName << "\n\n"; - } - } - else if (userInputs.output_file_type == "vtk") - { - // Write the results to separate files for each process - std::ofstream output(vtuFileName); - data_out.write_vtk(output); - pcout << "Output written to:" << vtuFileName << "\n\n"; - } - else - { - std::cerr << "PRISMS-PF Error: The parameter 'outputFileType' must be " - "either \"vtu\" or \"vtk\"\n"; - abort(); - } - - // log time - computing_timer.leave_subsection("matrixFreePDE: output"); -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/postprocessing/computeIntegral.cc b/src/core/postprocessing/computeIntegral.cc deleted file mode 100644 index e8b2e5a06..000000000 --- a/src/core/postprocessing/computeIntegral.cc +++ /dev/null @@ -1,65 +0,0 @@ - -#include - -template -void -MatrixFreePDE::computeIntegral( - double &integratedField, - int index, - std::vector *> variableSet) -{ - QGauss quadrature_formula(degree + 1); - FE_Q FE(QGaussLobatto<1>(degree + 1)); - FEValues fe_values(FE, - quadrature_formula, - update_values | update_JxW_values | update_quadrature_points); - const unsigned int n_q_points = quadrature_formula.size(); - std::vector cVal(n_q_points); - - // constraintsDirichletSet[index]->distribute(*variableSet[index]); - // constraintsOtherSet[index]->distribute(*variableSet[index]); - // variableSet[index]->update_ghost_values(); - - double value = 0.0; - - for (const auto &cell : dofHandlersSet[0]->active_cell_iterators()) - { - if (cell->is_locally_owned()) - { - fe_values.reinit(cell); - - fe_values.get_function_values(*variableSet[index], cVal); - - for (unsigned int q = 0; q < n_q_points; ++q) - { - value += (cVal[q]) * fe_values.JxW(q); - } - } - } - - value = Utilities::MPI::sum(value, MPI_COMM_WORLD); - - // if (Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0){ - // std::cout<<"Integrated field: "<; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/postprocessing/postprocessing.cc b/src/core/postprocessing/postprocessing.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/postprocessing/postprocessor.cc b/src/core/postprocessing/postprocessor.cc deleted file mode 100644 index 2f4775737..000000000 --- a/src/core/postprocessing/postprocessor.cc +++ /dev/null @@ -1,87 +0,0 @@ -#include - -template -void -MatrixFreePDE::computePostProcessedFields( - std::vector *> &postProcessedSet) -{ - // Initialize the postProcessedSet - for (unsigned int fieldIndex = 0; fieldIndex < pp_attributes.size(); fieldIndex++) - { - dealii::LinearAlgebra::distributed::Vector *U = nullptr; - U = new dealii::LinearAlgebra::distributed::Vector; - postProcessedSet.push_back(U); - matrixFreeObject.initialize_dof_vector(*U, 0); - } - - // call to integrate and assemble - matrixFreeObject.cell_loop(&MatrixFreePDE::getPostProcessedFields, - this, - postProcessedSet, - solutionSet, - true); -} - -template -void -MatrixFreePDE::getPostProcessedFields( - const dealii::MatrixFree &data, - std::vector *> &dst, - const std::vector *> &src, - const std::pair &cell_range) -{ - // initialize FEEvaulation objects - variableContainer> variable_list( - data, - userInputs.pp_baseVarInfoList); - variableContainer> - pp_variable_list(data, userInputs.pp_varInfoList, 0); - - // loop over cells - for (unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) - { - // Initialize, read DOFs, and set evaulation flags for each variable - variable_list.reinit_and_eval(src, cell); - pp_variable_list.reinit(cell); - - unsigned int num_q_points = variable_list.get_num_q_points(); - - dealii::VectorizedArray local_element_volume = element_volume[cell]; - - // loop over quadrature points - for (unsigned int q = 0; q < num_q_points; ++q) - { - variable_list.q_point = q; - pp_variable_list.q_point = q; - - dealii::Point> q_point_loc = - variable_list.get_q_point_location(); - - // Calculate the residuals - postProcessedFields(variable_list, - pp_variable_list, - q_point_loc, - local_element_volume); - } - - pp_variable_list.integrate_and_distribute(dst); - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/refinement/AdaptiveRefinement.cc b/src/core/refinement/AdaptiveRefinement.cc deleted file mode 100644 index 47cf0b469..000000000 --- a/src/core/refinement/AdaptiveRefinement.cc +++ /dev/null @@ -1,188 +0,0 @@ -#include - -using namespace dealii; - -template -AdaptiveRefinement::AdaptiveRefinement( - const userInputParameters &_userInputs, - parallel::distributed::Triangulation &_triangulation, - std::vector> &_fields, - std::vector *> &_solutionSet, - std::vector> *> &_soltransSet, - std::vector *> &_FESet, - std::vector *> &_dofHandlersSet_nonconst, - std::vector *> &_constraintsDirichletSet, - std::vector *> &_constraintsOtherSet) - : userInputs(_userInputs) - , triangulation(_triangulation) - , fields(_fields) - , solutionSet(_solutionSet) - , soltransSet(_soltransSet) - , FESet(_FESet) - , dofHandlersSet_nonconst(_dofHandlersSet_nonconst) - , constraintsDirichletSet(_constraintsDirichletSet) - , constraintsOtherSet(_constraintsOtherSet) -{} - -template -void -AdaptiveRefinement::do_adaptive_refinement(unsigned int currentIncrement) -{ - // Apply constraints for the initial condition so they are considered when remeshing - if (currentIncrement != 0) - { - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - constraintsDirichletSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - constraintsOtherSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - solutionSet[fieldIndex]->update_ghost_values(); - } - } - - adaptive_refinement_criterion(); - refine_grid(); -} - -template -void -AdaptiveRefinement::adaptive_refinement_criterion() -{ - QGaussLobatto quadrature(degree + 1); - const unsigned int num_quad_points = quadrature.size(); - - // Set the update flags - dealii::UpdateFlags update_flags = update_default; - for (const auto &criterion : userInputs.refinement_criteria) - { - if (criterion.criterion_type & criterion_value) - { - update_flags |= update_values; - } - else if (criterion.criterion_type & criterion_gradient) - { - update_flags |= update_gradients; - } - } - - FEValues fe_values(*FESet[userInputs.refinement_criteria[0].variable_index], - quadrature, - update_flags); - - std::vector values(num_quad_points); - std::vector gradient_magnitudes(num_quad_points); - std::vector> gradients(num_quad_points); - - typename parallel::distributed::Triangulation::active_cell_iterator t_cell = - triangulation.begin_active(); - - for (const auto &cell : - dofHandlersSet_nonconst[userInputs.refinement_criteria[0].variable_index] - ->active_cell_iterators()) - { - if (cell->is_locally_owned()) - { - fe_values.reinit(cell); - - bool mark_refine = false; - - // Loop through the refinement criteria to determine whether a cell needs to be - // refined or coarsened - for (const auto &criterion : userInputs.refinement_criteria) - { - // Get the values and/or gradients - if (static_cast(update_values & update_flags)) - { - fe_values.get_function_values(*solutionSet[criterion.variable_index], - values); - } - if (static_cast(update_gradients & update_flags)) - { - fe_values.get_function_gradients(*solutionSet[criterion.variable_index], - gradients); - - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - gradient_magnitudes[q_point] = gradients[q_point].norm(); - } - } - - // Loop through the quadrature points and determine if the cell needs to be - // refined - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - if (criterion.criterion_type & criterion_value && - values[q_point] > criterion.value_lower_bound && - values[q_point] < criterion.value_upper_bound) - { - mark_refine = true; - break; - } - if (criterion.criterion_type & criterion_gradient && - gradient_magnitudes[q_point] > criterion.gradient_lower_bound) - { - mark_refine = true; - break; - } - } - - // Early exit for when there are multiple refinement criteria - if (mark_refine) - { - break; - } - } - - // Limit the max and min refinement depth of the mesh - unsigned int current_level = t_cell->level(); - - if ((mark_refine && current_level < userInputs.max_refinement_level)) - { - cell->set_refine_flag(); - } - else if (!mark_refine && current_level > userInputs.min_refinement_level) - { - cell->set_coarsen_flag(); - } - } - ++t_cell; - } -} - -template -void -AdaptiveRefinement::refine_grid() -{ - // Prepare for refinement - triangulation.prepare_coarsening_and_refinement(); - - // Transfer solution - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - soltransSet[fieldIndex]->prepare_for_coarsening_and_refinement( - *solutionSet[fieldIndex]); - } - - // Execute refinement - triangulation.execute_coarsening_and_refinement(); -} - -// Explicit instantiation -template class AdaptiveRefinement<2, 1>; -template class AdaptiveRefinement<3, 1>; - -template class AdaptiveRefinement<2, 2>; -template class AdaptiveRefinement<3, 2>; - -template class AdaptiveRefinement<2, 3>; -template class AdaptiveRefinement<3, 3>; - -template class AdaptiveRefinement<2, 4>; -template class AdaptiveRefinement<3, 4>; - -template class AdaptiveRefinement<2, 5>; -template class AdaptiveRefinement<3, 5>; - -template class AdaptiveRefinement<2, 6>; -template class AdaptiveRefinement<3, 6>; \ No newline at end of file diff --git a/src/core/refinement/refinement.cc b/src/core/refinement/refinement.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/core/reinit.cc b/src/core/reinit.cc deleted file mode 100644 index 10085d93b..000000000 --- a/src/core/reinit.cc +++ /dev/null @@ -1,223 +0,0 @@ -// reinit() method for MatrixFreePDE class - -#include - -// populate with fields and setup matrix free system -template -void -MatrixFreePDE::reinit() -{ - computing_timer.enter_subsection("matrixFreePDE: reinitialization"); - - // setup system - pcout << "Reinitializing matrix free object\n"; - totalDOFs = 0; - for (const auto &field : fields) - { - currentFieldIndex = field.index; - - char buffer[100]; - - // create FESystem - FESystem *fe = nullptr; - fe = FESet.at(field.index); - - // distribute DOFs - DoFHandler *dof_handler = nullptr; - dof_handler = dofHandlersSet_nonconst.at(field.index); - - dof_handler->distribute_dofs(*fe); - totalDOFs += dof_handler->n_dofs(); - - // extract locally_relevant_dofs - IndexSet *locally_relevant_dofs = nullptr; - locally_relevant_dofs = locally_relevant_dofsSet_nonconst.at(field.index); - - locally_relevant_dofs->clear(); - DoFTools::extract_locally_relevant_dofs(*dof_handler, *locally_relevant_dofs); - - // create constraints - AffineConstraints *constraintsDirichlet = nullptr; - AffineConstraints *constraintsOther = nullptr; - - constraintsDirichlet = constraintsDirichletSet_nonconst.at(field.index); - constraintsOther = constraintsOtherSet_nonconst.at(field.index); - - constraintsDirichlet->clear(); - constraintsDirichlet->reinit(*locally_relevant_dofs); - constraintsOther->clear(); - constraintsOther->reinit(*locally_relevant_dofs); - - // Get hanging node constraints - DoFTools::make_hanging_node_constraints(*dof_handler, *constraintsOther); - - // Pin solution - if (userInputs.pinned_point.find(currentFieldIndex) != - userInputs.pinned_point.end()) - { - set_rigid_body_mode_constraints(constraintsOther, - dof_handler, - userInputs.pinned_point[currentFieldIndex]); - } - - // Get constraints for periodic BCs - setPeriodicityConstraints(constraintsOther, dof_handler); - - // Get constraints for Dirichlet BCs - applyDirichletBCs(); - - constraintsDirichlet->close(); - constraintsOther->close(); - - // Store Dirichlet BC DOF's - valuesDirichletSet[field.index]->clear(); - for (types::global_dof_index i = 0; i < dof_handler->n_dofs(); i++) - { - if (locally_relevant_dofs->is_element(i)) - { - if (constraintsDirichlet->is_constrained(i)) - { - (*valuesDirichletSet[field.index])[i] = - constraintsDirichlet->get_inhomogeneity(i); - } - } - } - - snprintf(buffer, - sizeof(buffer), - "field '%2s' DOF : %u (Constraint DOF : %u)\n", - field.name.c_str(), - dof_handler->n_dofs(), - constraintsDirichlet->n_constraints()); - pcout << buffer; - } - pcout << "total DOF : " << totalDOFs << "\n"; - - // Setup the matrix free object - typename MatrixFree::AdditionalData additional_data; - // The member "mpi_communicator" was removed in deal.II version 8.5 but is - // required before it - additional_data.tasks_parallel_scheme = - MatrixFree::AdditionalData::partition_partition; - // additional_data.tasks_parallel_scheme = - // MatrixFree::AdditionalData::none; - // additional_data.tasks_block_size = 1; // This improves performance for - // small runs, not sure about larger runs - additional_data.mapping_update_flags = - (update_values | update_gradients | update_JxW_values | update_quadrature_points); - QGaussLobatto<1> quadrature(degree + 1); - matrixFreeObject.clear(); - matrixFreeObject.reinit(MappingFE(FE_Q(QGaussLobatto<1>(degree + 1))), - dofHandlersSet, - constraintsOtherSet, - quadrature, - additional_data); - - bool dU_scalar_init = false; - bool dU_vector_init = false; - - // Setup solution vectors - pcout << "initializing parallel::distributed residual and solution vectors\n"; - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - dealii::LinearAlgebra::distributed::Vector *U = nullptr; - - U = solutionSet.at(fieldIndex); - - matrixFreeObject.initialize_dof_vector(*U, fieldIndex); - *U = 0; - - // Initializing temporary dU vector required for implicit solves of the - // elliptic equation. - if (fields[fieldIndex].pdetype == TIME_INDEPENDENT || - fields[fieldIndex].pdetype == IMPLICIT_TIME_DEPENDENT || - (fields[fieldIndex].pdetype == AUXILIARY && - var_attributes.at(fieldIndex).is_nonlinear)) - { - if (fields[fieldIndex].type == SCALAR) - { - if (!dU_scalar_init) - { - matrixFreeObject.initialize_dof_vector(dU_scalar, fieldIndex); - dU_scalar_init = true; - } - } - else - { - if (!dU_vector_init) - { - matrixFreeObject.initialize_dof_vector(dU_vector, fieldIndex); - dU_vector_init = true; - } - } - } - } - - // Compute invM in PDE is a time-dependent BVP - if (isTimeDependentBVP) - { - computeInvM(); - } - - // Transfer solution from previous mesh - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - // interpolate and clear used solution transfer sets - soltransSet[fieldIndex]->interpolate(*solutionSet[fieldIndex]); - delete soltransSet[fieldIndex]; - - // reset residual vector - dealii::LinearAlgebra::distributed::Vector *R = residualSet.at(fieldIndex); - matrixFreeObject.initialize_dof_vector(*R, fieldIndex); - *R = 0; - } - - // Create new solution transfer sets - soltransSet.clear(); - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - soltransSet.push_back( - new parallel::distributed:: - SolutionTransfer>( - *dofHandlersSet_nonconst[fieldIndex])); - } - - // If remeshing at the zeroth time step, re-apply initial conditions so the - // starting values are correct on the refined mesh - if (currentIncrement == 0 && !userInputs.load_grain_structure) - { - applyInitialConditions(); - } - - // Ghost the solution vectors. Also apply the Dirichet BC's (if any) on the - // solution vectors - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - constraintsDirichletSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - constraintsOtherSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - solutionSet[fieldIndex]->update_ghost_values(); - } - - // Once the initial triangulation has been set, compute element volume - compute_element_volume(); - - computing_timer.leave_subsection("matrixFreePDE: reinitialization"); -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/solvers/SolverParameters.cc b/src/core/solvers/SolverParameters.cc deleted file mode 100644 index 83253a561..000000000 --- a/src/core/solvers/SolverParameters.cc +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include - -SolverToleranceType -SolverParametersBase::getToleranceType(unsigned int index) -{ - return tolerance_type_list.at(getEquationIndex(index)); -} - -double -SolverParametersBase::getToleranceValue(unsigned int index) -{ - return tolerance_value_list.at(getEquationIndex(index)); -} - -unsigned int -SolverParametersBase::getEquationIndex(unsigned int global_index) -{ - for (unsigned int i = 0; i < var_index_list.size(); i++) - { - if (var_index_list.at(i) == global_index) - { - return i; - } - } - std::cerr << "PRISMS-PF Error: Attempted access of a parameter for the " - "nonlinear solver for an ineligible variable index.\n"; - abort(); -} - -void -LinearSolverParameters::loadParameters(unsigned int _var_index, - SolverToleranceType _tolerance_type, - double _tolerance_value, - unsigned int _max_iterations) -{ - var_index_list.push_back(_var_index); - tolerance_type_list.push_back(_tolerance_type); - tolerance_value_list.push_back(_tolerance_value); - max_iterations_list.push_back(_max_iterations); -} - -unsigned int -LinearSolverParameters::getMaxIterations(unsigned int index) -{ - return max_iterations_list.at(getEquationIndex(index)); -} - -void -NonlinearSolverParameters::loadParameters(unsigned int _var_index, - SolverToleranceType _tolerance_type, - double _tolerance_value, - bool _backtrack_damping_flag, - double _backtrack_step_modifier, - double _backtrack_residual_decrease_coeff, - double _default_dampling_coefficient, - bool _laplace_for_initial_guess) -{ - var_index_list.push_back(_var_index); - tolerance_type_list.push_back(_tolerance_type); - tolerance_value_list.push_back(_tolerance_value); - backtrack_damping_flag_list.push_back(_backtrack_damping_flag); - backtrack_step_modifier_list.push_back(_backtrack_step_modifier); - backtrack_residual_decrease_coeff_list.push_back(_backtrack_residual_decrease_coeff); - default_damping_coefficient_list.push_back(_default_dampling_coefficient); - laplace_for_initial_guess_list.push_back(_laplace_for_initial_guess); -} - -bool -NonlinearSolverParameters::getBacktrackDampingFlag(unsigned int index) -{ - return backtrack_damping_flag_list.at(getEquationIndex(index)); -} - -double -NonlinearSolverParameters::getBacktrackStepModifier(unsigned int index) -{ - return backtrack_step_modifier_list.at(getEquationIndex(index)); -} - -double -NonlinearSolverParameters::getBacktrackResidualDecreaseCoeff(unsigned int index) -{ - return backtrack_residual_decrease_coeff_list.at(getEquationIndex(index)); -} - -double -NonlinearSolverParameters::getDefaultDampingCoefficient(unsigned int index) -{ - return default_damping_coefficient_list.at(getEquationIndex(index)); -} - -bool -NonlinearSolverParameters::getLaplaceInitializationFlag(unsigned int index) -{ - return laplace_for_initial_guess_list.at(getEquationIndex(index)); -} - -void -NonlinearSolverParameters::setMaxIterations(unsigned int _max_iterations) -{ - max_iterations = _max_iterations; -} - -double -NonlinearSolverParameters::getMaxIterations() const -{ - return max_iterations; -} diff --git a/src/core/solvers/computeLHS.cc b/src/core/solvers/computeLHS.cc deleted file mode 100644 index b8b30ca12..000000000 --- a/src/core/solvers/computeLHS.cc +++ /dev/null @@ -1,110 +0,0 @@ -// vmult() and getLHS() method for MatrixFreePDE class - -#include - -// vmult operation for LHS -template - -void -MatrixFreePDE::vmult( - dealii::LinearAlgebra::distributed::Vector &dst, - const dealii::LinearAlgebra::distributed::Vector &src) const -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: computeLHS"); - - // create temporary copy of src vector as src2, as vector src is marked const - // and cannot be changed - dealii::LinearAlgebra::distributed::Vector src2; - matrixFreeObject.initialize_dof_vector(src2, currentFieldIndex); - src2 = src; - - // call cell_loop - if (!generatingInitialGuess) - { - matrixFreeObject.cell_loop(&MatrixFreePDE::getLHS, - this, - dst, - src2, - true); - } - else - { - matrixFreeObject.cell_loop(&MatrixFreePDE::getLaplaceLHS, - this, - dst, - src2, - true); - } - - // Account for Dirichlet BC's (essentially copy dirichlet DOF values present in src to - // dst, although it is unclear why the constraints can't just be distributed here) - for (auto &it : *valuesDirichletSet[currentFieldIndex]) - { - if (dst.in_local_range(it.first)) - { - dst(it.first) = src(it.first); //*jacobianDiagonal(it->first); - } - } - - // end log - computing_timer.leave_subsection("matrixFreePDE: computeLHS"); -} - -template -void -MatrixFreePDE::getLHS( - const MatrixFree &data, - dealii::LinearAlgebra::distributed::Vector &dst, - const dealii::LinearAlgebra::distributed::Vector &src, - const std::pair &cell_range) const -{ - variableContainer> - variable_list(data, userInputs.varInfoListLHS, userInputs.varChangeInfoListLHS); - - // loop over cells - for (unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) - { - // Initialize, read DOFs, and set evaulation flags for each variable - variable_list.reinit_and_eval(solutionSet, cell); - variable_list.reinit_and_eval_change_in_solution(src, cell, currentFieldIndex); - - unsigned int num_q_points = variable_list.get_num_q_points(); - - dealii::VectorizedArray local_element_volume = element_volume[cell]; - - // loop over quadrature points - for (unsigned int q = 0; q < num_q_points; ++q) - { - variable_list.q_point = q; - - dealii::Point> q_point_loc = - variable_list.get_q_point_location(); - - // Calculate the residuals - equationLHS(variable_list, q_point_loc, local_element_volume); - } - - // Integrate the residuals and distribute from local to global - variable_list.integrate_and_distribute_change_in_solution_LHS(dst, - currentFieldIndex); - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/solvers/computeRHS.cc b/src/core/solvers/computeRHS.cc deleted file mode 100644 index e69b071a3..000000000 --- a/src/core/solvers/computeRHS.cc +++ /dev/null @@ -1,136 +0,0 @@ -// computeRHS() method for MatrixFreePDE class - -#include -#include - -// update RHS of each field -template -void -MatrixFreePDE::computeExplicitRHS() -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: computeRHS"); - - // call to integrate and assemble while clearing residual vecotrs - matrixFreeObject.cell_loop(&MatrixFreePDE::getExplicitRHS, - this, - residualSet, - solutionSet, - true); - - // end log - computing_timer.leave_subsection("matrixFreePDE: computeRHS"); -} - -template -void -MatrixFreePDE::getExplicitRHS( - const MatrixFree &data, - std::vector *> &dst, - const std::vector *> &src, - const std::pair &cell_range) const -{ - variableContainer> variable_list( - data, - userInputs.varInfoListExplicitRHS); - - // loop over cells - for (unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) - { - // Initialize, read DOFs, and set evaulation flags for each variable - variable_list.reinit_and_eval(src, cell); - - unsigned int num_q_points = variable_list.get_num_q_points(); - - dealii::VectorizedArray local_element_volume = element_volume[cell]; - - // loop over quadrature points - for (unsigned int q = 0; q < num_q_points; ++q) - { - variable_list.q_point = q; - - dealii::Point> q_point_loc = - variable_list.get_q_point_location(); - - // Calculate the residuals - explicitEquationRHS(variable_list, q_point_loc, local_element_volume); - } - - variable_list.integrate_and_distribute(dst); - } -} - -// update RHS of each field -template -void -MatrixFreePDE::computeNonexplicitRHS() -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: computeRHS"); - - // call to integrate and assemble while clearing residual vecotrs - matrixFreeObject.cell_loop(&MatrixFreePDE::getNonexplicitRHS, - this, - residualSet, - solutionSet, - true); - - // end log - computing_timer.leave_subsection("matrixFreePDE: computeRHS"); -} - -template -void -MatrixFreePDE::getNonexplicitRHS( - const MatrixFree &data, - std::vector *> &dst, - const std::vector *> &src, - const std::pair &cell_range) const -{ - variableContainer> variable_list( - data, - userInputs.varInfoListNonexplicitRHS); - - // loop over cells - for (unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) - { - // Initialize, read DOFs, and set evaulation flags for each variable - variable_list.reinit_and_eval(src, cell); - - unsigned int num_q_points = variable_list.get_num_q_points(); - - dealii::VectorizedArray local_element_volume = element_volume[cell]; - - // loop over quadrature points - for (unsigned int q = 0; q < num_q_points; ++q) - { - variable_list.q_point = q; - - dealii::Point> q_point_loc = - variable_list.get_q_point_location(); - - // Calculate the residuals - nonExplicitEquationRHS(variable_list, q_point_loc, local_element_volume); - } - - variable_list.integrate_and_distribute(dst); - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/solvers/setNonlinearEqInitialGuess.cc b/src/core/solvers/setNonlinearEqInitialGuess.cc deleted file mode 100644 index 17e52f16e..000000000 --- a/src/core/solvers/setNonlinearEqInitialGuess.cc +++ /dev/null @@ -1,220 +0,0 @@ -// setNonlinearEqInitialGuess() method for MatrixFreePDE class -#include -#include - -#include -#include - -// solve each time increment -template -void -MatrixFreePDE::setNonlinearEqInitialGuess() -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: setNonlinearEqInitialGuess"); - Timer time; - time.start(); - char buffer[200]; - - for (const auto &[fieldIndex, variable] : var_attributes) - { - if ((variable.eq_type == TIME_INDEPENDENT) && variable.is_nonlinear && - userInputs.nonlinear_solver_parameters.getLaplaceInitializationFlag(fieldIndex)) - { - currentFieldIndex = fieldIndex; // Used in computeLaplaceLHS() - - computeLaplaceRHS(fieldIndex); - - for (const auto &it : *valuesDirichletSet[fieldIndex]) - { - if (residualSet[fieldIndex]->in_local_range(it.first)) - { - (*residualSet[fieldIndex])(it.first) = 0.0; - } - } - - // solver controls - double tol_value = NAN; - if (userInputs.linear_solver_parameters.getToleranceType(fieldIndex) == - ABSOLUTE_RESIDUAL) - { - tol_value = - userInputs.linear_solver_parameters.getToleranceValue(fieldIndex); - } - else - { - tol_value = - userInputs.linear_solver_parameters.getToleranceValue(fieldIndex) * - residualSet[fieldIndex]->l2_norm(); - } - - SolverControl solver_control( - userInputs.linear_solver_parameters.getMaxIterations(fieldIndex), - tol_value); - - // Currently the only allowed solver is SolverCG, the SolverType input - // variable is a dummy - SolverCG> solver( - solver_control); - - // solve - try - { - if (fields[fieldIndex].type == SCALAR) - { - dU_scalar = 0.0; - solver.solve(*this, - dU_scalar, - *residualSet[fieldIndex], - IdentityMatrix(solutionSet[fieldIndex]->size())); - } - else - { - dU_vector = 0.0; - solver.solve(*this, - dU_vector, - *residualSet[fieldIndex], - IdentityMatrix(solutionSet[fieldIndex]->size())); - } - } - catch (...) - { - pcout << "\nWarning: implicit solver did not converge as per set " - "tolerances. consider increasing maxSolverIterations or " - "decreasing solverTolerance.\n"; - } - - if (fields[fieldIndex].type == SCALAR) - { - *solutionSet[fieldIndex] += dU_scalar; - } - else - { - *solutionSet[fieldIndex] += dU_vector; - } - - if (currentIncrement % userInputs.skip_print_steps == 0) - { - double dU_norm = NAN; - if (fields[fieldIndex].type == SCALAR) - { - dU_norm = dU_scalar.l2_norm(); - } - else - { - dU_norm = dU_vector.l2_norm(); - } - snprintf(buffer, - sizeof(buffer), - "field '%2s' [laplace solve for initial guess]: initial " - "residual:%12.6e, current residual:%12.6e, nsteps:%u, " - "tolerance criterion:%12.6e, solution: %12.6e, dU: %12.6e\n", - fields[fieldIndex].name.c_str(), - residualSet[fieldIndex]->l2_norm(), - solver_control.last_value(), - solver_control.last_step(), - solver_control.tolerance(), - solutionSet[fieldIndex]->l2_norm(), - dU_norm); - pcout << buffer << "\n"; - } - } - } - - if (currentIncrement % userInputs.skip_print_steps == 0) - { - pcout << "wall time: " << time.wall_time() << "s\n"; - } - // log time - computing_timer.leave_subsection("matrixFreePDE: setNonlinearEqInitialGuess"); -} - -template -void -MatrixFreePDE::computeLaplaceRHS(unsigned int fieldIndex) -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: computeLaplaceRHS"); - - // call to integrate and assemble while clearing residual vecotrs - matrixFreeObject.cell_loop(&MatrixFreePDE::getLaplaceRHS, - this, - *residualSet[fieldIndex], - *solutionSet[fieldIndex], - true); - - // end log - computing_timer.leave_subsection("matrixFreePDE: computeLaplaceRHS"); -} - -template -void -MatrixFreePDE::getLaplaceRHS( - const MatrixFree &data, - dealii::LinearAlgebra::distributed::Vector &dst, - const dealii::LinearAlgebra::distributed::Vector &src, - const std::pair &cell_range) const -{ - FEEvaluation mat(data); - - dealii::EvaluationFlags::EvaluationFlags laplace_flags = - dealii::EvaluationFlags::gradients; - // loop over all "cells" - for (unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) - { - mat.reinit(cell); - mat.read_dof_values(src); - mat.evaluate(laplace_flags); - for (unsigned int q = 0; q < mat.n_q_points; ++q) - { - mat.submit_gradient(mat.get_gradient(q), q); - } - mat.integrate(laplace_flags); - mat.distribute_local_to_global(dst); - } -} - -template -void -MatrixFreePDE::getLaplaceLHS( - const MatrixFree &data, - dealii::LinearAlgebra::distributed::Vector &dst, - const dealii::LinearAlgebra::distributed::Vector &src, - const std::pair &cell_range) const -{ - FEEvaluation mat(data); - - dealii::EvaluationFlags::EvaluationFlags laplace_flags = - dealii::EvaluationFlags::gradients; - // loop over all "cells" - for (unsigned int cell = cell_range.first; cell < cell_range.second; ++cell) - { - mat.reinit(cell); - mat.read_dof_values(src); - mat.evaluate(laplace_flags); - for (unsigned int q = 0; q < mat.n_q_points; ++q) - { - mat.submit_gradient(-mat.get_gradient(q), q); - } - mat.integrate(laplace_flags); - mat.distribute_local_to_global(dst); - } -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/solvers/solve.cc b/src/core/solvers/solve.cc index 48a083e3e..e69de29bb 100644 --- a/src/core/solvers/solve.cc +++ b/src/core/solvers/solve.cc @@ -1,172 +0,0 @@ -// solve() method for MatrixFreePDE class - -#include - -// solve BVP -template -void -MatrixFreePDE::solve() -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: solve"); - pcout << "\nsolving...\n\n"; - - // time dependent BVP - if (isTimeDependentBVP) - { - // If grain reassignment is activated, reassign grains - if (userInputs.grain_remapping_activated and - (currentIncrement % userInputs.skip_grain_reassignment_steps == 0 or - currentIncrement == 0)) - { - reassignGrains(); - } - - // For any nonlinear equation, set the initial guess as the solution to - // Laplace's equations - generatingInitialGuess = true; - setNonlinearEqInitialGuess(); - generatingInitialGuess = false; - - // Do an initial solve to set the elliptic fields - solveIncrement(true); - - // output initial conditions for time dependent BVP - if (userInputs.outputTimeStepList[currentOutput] == currentIncrement) - { - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - constraintsDirichletSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - constraintsOtherSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - solutionSet[fieldIndex]->update_ghost_values(); - } - outputResults(); - currentOutput++; - } - - if (userInputs.checkpointTimeStepList[currentCheckpoint] == currentIncrement) - { - save_checkpoint(); - currentCheckpoint++; - } - - // Increase the current increment from 0 to 1 now that the initial - // conditions have been output - currentIncrement++; - - // Cycle up to the proper output and checkpoint counters - while (userInputs.outputTimeStepList.size() > 0 && - userInputs.outputTimeStepList[currentOutput] < currentIncrement) - { - currentOutput++; - } - while (userInputs.checkpointTimeStepList.size() > 0 && - userInputs.checkpointTimeStepList[currentCheckpoint] < currentIncrement) - { - currentCheckpoint++; - } - - // time stepping - pcout << "\nTime stepping parameters: timeStep: " << userInputs.dtValue - << " timeFinal: " << userInputs.finalTime - << " timeIncrements: " << userInputs.totalIncrements << "\n"; - - // This is the main time-stepping loop - for (; currentIncrement <= userInputs.totalIncrements; ++currentIncrement) - { - // increment current time - currentTime += userInputs.dtValue; - if (currentIncrement % userInputs.skip_print_steps == 0) - { - pcout << "\ntime increment:" << currentIncrement - << " time: " << currentTime << "\n"; - } - - // check and perform adaptive mesh refinement - if (userInputs.h_adaptivity == true && - currentIncrement % userInputs.skip_remeshing_steps == 0) - { - computing_timer.enter_subsection("matrixFreePDE: AMR"); - - AMR.do_adaptive_refinement(currentIncrement); - reinit(); - - computing_timer.leave_subsection("matrixFreePDE: AMR"); - } - - // Update the list of nuclei (if relevant) - updateNucleiList(); - - // If grain reassignment is activated, reassign grains - if (userInputs.grain_remapping_activated and - (currentIncrement % userInputs.skip_grain_reassignment_steps == 0 or - currentIncrement == 0)) - { - reassignGrains(); - } - - // solve time increment - solveIncrement(false); - - // Output results to file (on the proper increments) - if (userInputs.outputTimeStepList[currentOutput] == currentIncrement) - { - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - constraintsDirichletSet[fieldIndex]->distribute( - *solutionSet[fieldIndex]); - constraintsOtherSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - solutionSet[fieldIndex]->update_ghost_values(); - } - outputResults(); - if (userInputs.print_timing_with_output && - currentIncrement < userInputs.totalIncrements) - { - computing_timer.print_summary(); - } - - currentOutput++; - } - - // Create a checkpoint (on the proper increments) - if (userInputs.checkpointTimeStepList[currentCheckpoint] == currentIncrement) - { - save_checkpoint(); - currentCheckpoint++; - } - } - } - - // time independent BVP - else - { - generatingInitialGuess = false; - - // solve - solveIncrement(false); - - // output results to file - outputResults(); - } - - // log time - computing_timer.leave_subsection("matrixFreePDE: solve"); -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/solvers/solveIncrement.cc b/src/core/solvers/solveIncrement.cc deleted file mode 100644 index 58d91399b..000000000 --- a/src/core/solvers/solveIncrement.cc +++ /dev/null @@ -1,570 +0,0 @@ -#include - -#include -#include -#include - -// solve each time increment -template -void -MatrixFreePDE::solveIncrement(bool skip_time_dependent) -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: solveIncrements"); - Timer time; - char buffer[200]; - - // Get the RHS of the explicit equations - if (hasExplicitEquation && !skip_time_dependent) - { - computeExplicitRHS(); - } - - // solve for each field - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - currentFieldIndex = fieldIndex; // Used in computeLHS() - - // Parabolic (first order derivatives in time) fields - if (fields[fieldIndex].pdetype == EXPLICIT_TIME_DEPENDENT && !skip_time_dependent) - { - updateExplicitSolution(fieldIndex); - - // Apply Boundary conditions - applyBCs(fieldIndex); - - // Print update to screen and confirm that solution isn't nan - if (currentIncrement % userInputs.skip_print_steps == 0) - { - double solution_L2_norm = solutionSet[fieldIndex]->l2_norm(); - - snprintf(buffer, - sizeof(buffer), - "field '%2s' [explicit solve]: current solution: " - "%12.6e, current residual:%12.6e\n", - fields[fieldIndex].name.c_str(), - solution_L2_norm, - residualSet[fieldIndex]->l2_norm()); - pcout << buffer; - - if (!numbers::is_finite(solution_L2_norm)) - { - snprintf(buffer, - sizeof(buffer), - "ERROR: field '%s' solution is NAN. exiting.\n\n", - fields[fieldIndex].name.c_str()); - pcout << buffer; - exit(-1); - } - } - } - } - - // Now, update the non-explicit variables - // For the time being, this is just the elliptic equations, but implicit - // parabolic and auxilary equations should also be here - if (hasNonExplicitEquation) - { - bool nonlinear_iteration_converged = false; - unsigned int nonlinear_iteration_index = 0; - - while (!nonlinear_iteration_converged) - { - nonlinear_iteration_converged = true; - - // Update residualSet for the non-explicitly updated variables - computeNonexplicitRHS(); - - for (const auto &[fieldIndex, variable] : var_attributes) - { - currentFieldIndex = fieldIndex; // Used in computeLHS() - - if ((fields[fieldIndex].pdetype == IMPLICIT_TIME_DEPENDENT && - !skip_time_dependent) || - fields[fieldIndex].pdetype == TIME_INDEPENDENT) - { - if (currentIncrement % userInputs.skip_print_steps == 0 && - variable.is_nonlinear) - { - snprintf(buffer, - sizeof(buffer), - "field '%2s' [nonlinear solve]: current " - "solution: %12.6e, current residual:%12.6e\n", - fields[fieldIndex].name.c_str(), - solutionSet[fieldIndex]->l2_norm(), - residualSet[fieldIndex]->l2_norm()); - pcout << buffer; - } - - nonlinear_iteration_converged = - updateImplicitSolution(fieldIndex, nonlinear_iteration_index); - - // Apply Boundary conditions - applyBCs(fieldIndex); - } - else if (fields[fieldIndex].pdetype == AUXILIARY) - { - if (variable.is_nonlinear || nonlinear_iteration_index == 0) - { - // If the equation for this field is nonlinear, save the old - // solution - if (variable.is_nonlinear) - { - if (fields[fieldIndex].type == SCALAR) - { - dU_scalar = *solutionSet[fieldIndex]; - } - else - { - dU_vector = *solutionSet[fieldIndex]; - } - } - - updateExplicitSolution(fieldIndex); - - // Apply Boundary conditions - applyBCs(fieldIndex); - - // Print update to screen - if (currentIncrement % userInputs.skip_print_steps == 0) - { - snprintf(buffer, - sizeof(buffer), - "field '%2s' [auxiliary solve]: current solution: " - "%12.6e, current residual:%12.6e\n", - fields[fieldIndex].name.c_str(), - solutionSet[fieldIndex]->l2_norm(), - residualSet[fieldIndex]->l2_norm()); - pcout << buffer; - } - - // Check to see if this individual variable has converged - if (variable.is_nonlinear) - { - if (userInputs.nonlinear_solver_parameters.getToleranceType( - fieldIndex) == ABSOLUTE_SOLUTION_CHANGE) - { - double diff = NAN; - - if (fields[fieldIndex].type == SCALAR) - { - dU_scalar -= *solutionSet[fieldIndex]; - diff = dU_scalar.l2_norm(); - } - else - { - dU_vector -= *solutionSet[fieldIndex]; - diff = dU_vector.l2_norm(); - } - if (currentIncrement % userInputs.skip_print_steps == 0) - { - snprintf(buffer, - sizeof(buffer), - " field '%2s' [nonlinear solve] current " - "increment: %u, nonlinear " - "iteration: " - "%u, dU: %12.6e\n", - fields[fieldIndex].name.c_str(), - currentIncrement, - nonlinear_iteration_index, - diff); - pcout << buffer; - } - - if (diff > userInputs.nonlinear_solver_parameters - .getToleranceValue(fieldIndex) && - nonlinear_iteration_index < - userInputs.nonlinear_solver_parameters - .getMaxIterations()) - { - nonlinear_iteration_converged = false; - } - } - else - { - AssertThrow( - false, - FeatureNotImplemented( - "Nonlinear solver tolerances besides ABSOLUTE_CHANGE")); - } - } - } - } - - // check if solution is nan - if (!numbers::is_finite(solutionSet[fieldIndex]->l2_norm())) - { - snprintf(buffer, - sizeof(buffer), - "ERROR: field '%s' solution is NAN. exiting.\n\n", - fields[fieldIndex].name.c_str()); - pcout << buffer; - exit(-1); - } - } - - nonlinear_iteration_index++; - } - } - - if (currentIncrement % userInputs.skip_print_steps == 0) - { - pcout << "wall time: " << time.wall_time() << "s\n"; - } - // log time - computing_timer.leave_subsection("matrixFreePDE: solveIncrements"); -} - -// Application of boundary conditions -template -void -MatrixFreePDE::applyBCs(unsigned int fieldIndex) -{ - // Add Neumann BCs - if (fields[fieldIndex].hasNeumannBCs) - { - // Currently commented out because it isn't working yet - // applyNeumannBCs(); - } - - // Set the Dirichelet values (hanging node constraints don't need to be distributed - // every time step, only at output) - if (fields[fieldIndex].hasDirichletBCs) - { - // Apply non-uniform Dirlichlet_BCs to the current field - if (fields[fieldIndex].hasnonuniformDirichletBCs) - { - DoFHandler *dof_handler = nullptr; - dof_handler = dofHandlersSet_nonconst.at(currentFieldIndex); - IndexSet *locally_relevant_dofs = nullptr; - locally_relevant_dofs = locally_relevant_dofsSet_nonconst.at(currentFieldIndex); - locally_relevant_dofs->clear(); - DoFTools::extract_locally_relevant_dofs(*dof_handler, *locally_relevant_dofs); - AffineConstraints *constraintsDirichlet = nullptr; - constraintsDirichlet = constraintsDirichletSet_nonconst.at(currentFieldIndex); - constraintsDirichlet->clear(); - constraintsDirichlet->reinit(*locally_relevant_dofs); - applyDirichletBCs(); - constraintsDirichlet->close(); - } - // Distribute for Uniform or Non-Uniform Dirichlet BCs - constraintsDirichletSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - } - solutionSet[fieldIndex]->update_ghost_values(); -} - -// Explicit time step for matrixfree solve -template -void -MatrixFreePDE::updateExplicitSolution(unsigned int fieldIndex) -{ - // Explicit-time step each DOF - // Takes advantage of knowledge that the length of solutionSet and residualSet - // is an integer multiple of the length of invM for vector variables - if (fields[fieldIndex].type == SCALAR) - { - unsigned int invM_size = invMscalar.locally_owned_size(); - for (unsigned int dof = 0; dof < solutionSet[fieldIndex]->locally_owned_size(); - ++dof) - { - solutionSet[fieldIndex]->local_element(dof) = - invMscalar.local_element(dof % invM_size) * - residualSet[fieldIndex]->local_element(dof); - } - } - else if (fields[fieldIndex].type == VECTOR) - { - unsigned int invM_size = invMvector.locally_owned_size(); - for (unsigned int dof = 0; dof < solutionSet[fieldIndex]->locally_owned_size(); - ++dof) - { - solutionSet[fieldIndex]->local_element(dof) = - invMvector.local_element(dof % invM_size) * - residualSet[fieldIndex]->local_element(dof); - } - } -} - -template -bool -MatrixFreePDE::updateImplicitSolution(unsigned int fieldIndex, - unsigned int nonlinear_iteration_index) -{ - char buffer[200]; - - // Assume convergence criterion is met, unless otherwise proven later on. - bool nonlinear_iteration_converged = true; - - // Apply Dirichlet BC's. This clears the residual where we want to apply Dirichlet BCs, - // otherwise the solver sees a positive residual - constraintsDirichletSet[fieldIndex]->set_zero(*residualSet[fieldIndex]); - - // Grab solver controls - double tol_value = NAN; - if (userInputs.linear_solver_parameters.getToleranceType(fieldIndex) == - ABSOLUTE_RESIDUAL) - { - tol_value = userInputs.linear_solver_parameters.getToleranceValue(fieldIndex); - } - else - { - tol_value = userInputs.linear_solver_parameters.getToleranceValue(fieldIndex) * - residualSet[fieldIndex]->l2_norm(); - } - - SolverControl solver_control(userInputs.linear_solver_parameters.getMaxIterations( - fieldIndex), - tol_value); - - // Currently the only allowed solver is SolverCG, the - // SolverType input variable is a dummy - SolverCG> solver(solver_control); - - // Solve - try - { - if (fields[fieldIndex].type == SCALAR) - { - dU_scalar = 0.0; - solver.solve(*this, - dU_scalar, - *residualSet[fieldIndex], - IdentityMatrix(solutionSet[fieldIndex]->size())); - } - else - { - dU_vector = 0.0; - solver.solve(*this, - dU_vector, - *residualSet[fieldIndex], - IdentityMatrix(solutionSet[fieldIndex]->size())); - } - } - catch (...) - { - pcout << "\nWarning: linear solver did not converge as " - "per set tolerances. consider increasing the " - "maximum number of iterations or decreasing the " - "solver tolerance.\n"; - } - - if (var_attributes.at(fieldIndex).is_nonlinear) - { - // Now that we have the calculated change in the solution, - // we need to select a damping coefficient - double damping_coefficient = NAN; - - if (userInputs.nonlinear_solver_parameters.getBacktrackDampingFlag(fieldIndex)) - { - dealii::LinearAlgebra::distributed::Vector solutionSet_old = - *solutionSet[fieldIndex]; - double residual_old = residualSet[fieldIndex]->l2_norm(); - - damping_coefficient = 1.0; - bool damping_coefficient_found = false; - while (!damping_coefficient_found) - { - if (fields[fieldIndex].type == SCALAR) - { - solutionSet[fieldIndex]->sadd(1.0, damping_coefficient, dU_scalar); - } - else - { - solutionSet[fieldIndex]->sadd(1.0, damping_coefficient, dU_vector); - } - - computeNonexplicitRHS(); - - for (const auto &it : *valuesDirichletSet[fieldIndex]) - { - if (residualSet[fieldIndex]->in_local_range(it.first)) - { - (*residualSet[fieldIndex])(it.first) = 0.0; - } - } - - double residual_new = residualSet[fieldIndex]->l2_norm(); - - if (currentIncrement % userInputs.skip_print_steps == 0) - { - pcout << " Old residual: " << residual_old - << " Damping Coeff: " << damping_coefficient - << " New Residual: " << residual_new << "\n"; - } - - // An improved approach would use the - // Armijo–Goldstein condition to ensure a - // sufficent decrease in the residual. This way is - // just scales the residual. - if ((residual_new < - (residual_old * userInputs.nonlinear_solver_parameters - .getBacktrackResidualDecreaseCoeff(fieldIndex))) || - damping_coefficient < 1.0e-4) - { - damping_coefficient_found = true; - } - else - { - damping_coefficient *= - userInputs.nonlinear_solver_parameters.getBacktrackStepModifier( - fieldIndex); - *solutionSet[fieldIndex] = solutionSet_old; - } - } - } - else - { - damping_coefficient = - userInputs.nonlinear_solver_parameters.getDefaultDampingCoefficient( - fieldIndex); - - if (fields[fieldIndex].type == SCALAR) - { - solutionSet[fieldIndex]->sadd(1.0, damping_coefficient, dU_scalar); - } - else - { - solutionSet[fieldIndex]->sadd(1.0, damping_coefficient, dU_vector); - } - } - - if (currentIncrement % userInputs.skip_print_steps == 0) - { - double dU_norm = NAN; - if (fields[fieldIndex].type == SCALAR) - { - dU_norm = dU_scalar.l2_norm(); - } - else - { - dU_norm = dU_vector.l2_norm(); - } - snprintf(buffer, - sizeof(buffer), - "field '%2s' [linear solve]: initial " - "residual:%12.6e, current residual:%12.6e, " - "nsteps:%u, tolerance criterion:%12.6e, " - "solution: %12.6e, dU: %12.6e\n", - fields[fieldIndex].name.c_str(), - residualSet[fieldIndex]->l2_norm(), - solver_control.last_value(), - solver_control.last_step(), - solver_control.tolerance(), - solutionSet[fieldIndex]->l2_norm(), - dU_norm); - pcout << buffer; - } - - // Check to see if this individual variable has converged - if (userInputs.nonlinear_solver_parameters.getToleranceType(fieldIndex) == - ABSOLUTE_SOLUTION_CHANGE) - { - double diff = NAN; - - if (fields[fieldIndex].type == SCALAR) - { - diff = dU_scalar.l2_norm(); - } - else - { - diff = dU_vector.l2_norm(); - } - if (currentIncrement % userInputs.skip_print_steps == 0) - { - snprintf(buffer, - sizeof(buffer), - " field '%2s' [nonlinear solve] current increment: %u, nonlinear " - "iteration: " - "%u, dU: %12.6e\n", - fields[fieldIndex].name.c_str(), - currentIncrement, - nonlinear_iteration_index, - diff); - pcout << buffer; - } - - if (diff > - userInputs.nonlinear_solver_parameters.getToleranceValue(fieldIndex) && - nonlinear_iteration_index < - userInputs.nonlinear_solver_parameters.getMaxIterations()) - { - nonlinear_iteration_converged = false; - } - else if (diff > - userInputs.nonlinear_solver_parameters.getToleranceValue(fieldIndex)) - { - pcout << "\nWarning: nonlinear solver did not converge as " - "per set tolerances. consider increasing the " - "maximum number of iterations or decreasing the " - "solver tolerance.\n"; - } - } - else - { - AssertThrow(false, - FeatureNotImplemented( - "Nonlinear solver tolerances besides ABSOLUTE_CHANGE")); - } - } - else - { - if (nonlinear_iteration_index == 0) - { - if (fields[fieldIndex].type == SCALAR) - { - *solutionSet[fieldIndex] += dU_scalar; - } - else - { - *solutionSet[fieldIndex] += dU_vector; - } - - if (currentIncrement % userInputs.skip_print_steps == 0) - { - double dU_norm = NAN; - if (fields[fieldIndex].type == SCALAR) - { - dU_norm = dU_scalar.l2_norm(); - } - else - { - dU_norm = dU_vector.l2_norm(); - } - snprintf(buffer, - sizeof(buffer), - "field '%2s' [linear solve]: initial " - "residual:%12.6e, current residual:%12.6e, " - "nsteps:%u, tolerance criterion:%12.6e, " - "solution: %12.6e, dU: %12.6e\n", - fields[fieldIndex].name.c_str(), - residualSet[fieldIndex]->l2_norm(), - solver_control.last_value(), - solver_control.last_step(), - solver_control.tolerance(), - solutionSet[fieldIndex]->l2_norm(), - dU_norm); - pcout << buffer; - } - } - } - - return nonlinear_iteration_converged; -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/core/userInputParameters.cc b/src/core/userInputParameters.cc deleted file mode 100644 index 10e50357f..000000000 --- a/src/core/userInputParameters.cc +++ /dev/null @@ -1,1557 +0,0 @@ -#include -#include - -#include -#include - -template -userInputParameters::userInputParameters(inputFileReader &input_file_reader, - dealii::ParameterHandler ¶meter_handler) - : var_attributes(input_file_reader.var_attributes) - , pp_attributes(input_file_reader.pp_attributes) -{ - loadVariableAttributes(); - - // Spatial discretization - assign_spatial_discretization_parameters(parameter_handler); - - // Time stepping parameters - assign_temporal_discretization_parameters(parameter_handler); - - // Linear solver parameters - assign_linear_solve_parameters(parameter_handler); - - // Non-linear solver parameters - assign_nonlinear_solve_parameters(parameter_handler); - - // Output parameters - assign_output_parameters(parameter_handler); - - // Initial condition parameters - assign_load_initial_condition_parameters(parameter_handler); - - // Nucleation parameters - assign_nucleation_parameters(parameter_handler); - - // Grain remapping & vtk load-in parameters - assign_grain_parameters(parameter_handler); - - // Boundary conditions - assign_boundary_condition_parameters(parameter_handler); - - // Load the user-defined constants - load_model_constants(input_file_reader, parameter_handler); -} - -template -void -userInputParameters::loadVariableAttributes() -{ - // Load some nucleation parameters - for (const auto &[index, variable] : var_attributes) - { - if (variable.nucleating_variable) - { - nucleating_variable_indices.push_back(index); - } - if (variable.need_value_nucleation || variable.nucleating_variable) - { - nucleation_need_value.push_back(index); - } - } - - nucleation_occurs = !nucleating_variable_indices.empty(); - - // Load variable information for calculating the RHS for explicit equations - num_var_explicit_RHS = 0; - for (const auto &[index, variable] : var_attributes) - { - if (!static_cast(variable.eval_flags_explicit_RHS & - dealii::EvaluationFlags::nothing)) - { - num_var_explicit_RHS++; - } - } - varInfoListExplicitRHS.reserve(num_var_explicit_RHS); - for (const auto &[index, variable] : var_attributes) - { - variable_info varInfo {}; - - varInfo.evaluation_flags = variable.eval_flags_explicit_RHS; - - varInfo.residual_flags = variable.eval_flags_residual_explicit_RHS; - - varInfo.global_var_index = index; - - varInfo.var_needed = - !static_cast(varInfo.evaluation_flags & dealii::EvaluationFlags::nothing); - - varInfo.is_scalar = variable.var_type == SCALAR; - - varInfoListExplicitRHS.push_back(varInfo); - } - - // Load variable information for calculating the RHS for nonexplicit equations - num_var_nonexplicit_RHS = 0; - for (const auto &[index, variable] : var_attributes) - { - if (!static_cast(variable.eval_flags_nonexplicit_RHS & - dealii::EvaluationFlags::nothing)) - { - num_var_nonexplicit_RHS++; - } - } - varInfoListNonexplicitRHS.reserve(num_var_nonexplicit_RHS); - for (const auto &[index, variable] : var_attributes) - { - variable_info varInfo {}; - - varInfo.evaluation_flags = variable.eval_flags_nonexplicit_RHS; - - varInfo.residual_flags = variable.eval_flags_residual_nonexplicit_RHS; - - varInfo.global_var_index = index; - - varInfo.var_needed = - !static_cast(varInfo.evaluation_flags & dealii::EvaluationFlags::nothing); - - varInfo.is_scalar = variable.var_type == SCALAR; - - varInfoListNonexplicitRHS.push_back(varInfo); - } - - // Load variable information for calculating the LHS - num_var_LHS = 0; - for (const auto &[index, variable] : var_attributes) - { - if (!static_cast(variable.eval_flags_nonexplicit_LHS & - dealii::EvaluationFlags::nothing)) - { - num_var_LHS++; - } - } - - varInfoListLHS.reserve(num_var_LHS); - for (const auto &[index, variable] : var_attributes) - { - variable_info varInfo {}; - - varInfo.evaluation_flags = variable.eval_flags_nonexplicit_LHS; - - varInfo.residual_flags = variable.eval_flags_residual_nonexplicit_LHS; - - varInfo.global_var_index = index; - - varInfo.var_needed = - !static_cast(varInfo.evaluation_flags & dealii::EvaluationFlags::nothing); - - varInfo.is_scalar = variable.var_type == SCALAR; - - varInfoListLHS.push_back(varInfo); - } - - varChangeInfoListLHS.reserve(num_var_LHS); - for (const auto &[index, variable] : var_attributes) - { - variable_info varInfo {}; - - varInfo.evaluation_flags = variable.eval_flags_change_nonexplicit_LHS; - - // FOR NOW, TAKING THESE FROM THE VARIABLE ITSELF!! - varInfo.residual_flags = variable.eval_flags_residual_nonexplicit_LHS; - - varInfo.global_var_index = index; - - varInfo.var_needed = - !static_cast(varInfo.evaluation_flags & dealii::EvaluationFlags::nothing); - - varInfo.is_scalar = variable.var_type == SCALAR; - - varChangeInfoListLHS.push_back(varInfo); - } - - // Load variable information for postprocessing - // First, the info list for the base field variables - pp_baseVarInfoList.reserve(var_attributes.size()); - for (const auto &[index, variable] : var_attributes) - { - variable_info varInfo {}; - - varInfo.evaluation_flags = variable.eval_flags_postprocess; - - varInfo.global_var_index = index; - - varInfo.var_needed = - !static_cast(varInfo.evaluation_flags & dealii::EvaluationFlags::nothing); - - varInfo.is_scalar = variable.var_type == SCALAR; - - pp_baseVarInfoList.push_back(varInfo); - } - - // Now load the information for the post-processing variables - // Parameters for postprocessing - - postProcessingRequired = !pp_attributes.empty(); - - num_integrated_fields = 0; - for (const auto &[pp_index, pp_variable] : pp_attributes) - { - if (pp_variable.calc_integral) - { - num_integrated_fields++; - integrated_field_indices.push_back(pp_index); - } - } - - // The info list for the postprocessing field variables - pp_varInfoList.reserve(pp_attributes.size()); - for (const auto &[pp_index, pp_variable] : pp_attributes) - { - variable_info varInfo {}; - - varInfo.var_needed = true; - - varInfo.residual_flags = pp_variable.eval_flags_residual_postprocess; - - varInfo.global_var_index = pp_index; - - varInfo.is_scalar = pp_variable.var_type == SCALAR; - - pp_varInfoList.push_back(varInfo); - } -} - -template -void -userInputParameters::assign_spatial_discretization_parameters( - dealii::ParameterHandler ¶meter_handler) -{ - // Domain size & subdivisions - domain_size.push_back(parameter_handler.get_double("Domain size X")); - subdivisions.push_back(parameter_handler.get_integer("Subdivisions X")); - - if (dim > 1) - { - domain_size.push_back(parameter_handler.get_double("Domain size Y")); - subdivisions.push_back(parameter_handler.get_integer("Subdivisions Y")); - - if (dim > 2) - { - domain_size.push_back(parameter_handler.get_double("Domain size Z")); - subdivisions.push_back(parameter_handler.get_integer("Subdivisions Z")); - } - } - - refine_factor = parameter_handler.get_integer("Refine factor"); - - degree = parameter_handler.get_integer("Element degree"); - - // Adaptive meshing parameters - h_adaptivity = parameter_handler.get_bool("Mesh adaptivity"); - skip_remeshing_steps = - parameter_handler.get_integer("Steps between remeshing operations"); - - max_refinement_level = parameter_handler.get_integer("Max refinement level"); - min_refinement_level = parameter_handler.get_integer("Min refinement level"); - - // Enforce that the initial refinement level must be between the max and min - // level - if (h_adaptivity && - ((refine_factor < min_refinement_level) || (refine_factor > max_refinement_level))) - { - std::cerr << "PRISMS-PF Error: The initial refinement factor must be " - "between the maximum and minimum refinement levels when " - "adaptive meshing is enabled.\n"; - std::cerr << "Initial refinement level: " << refine_factor - << " Maximum and minimum refinement levels: " << max_refinement_level - << ", " << min_refinement_level << "\n"; - abort(); - } - - // The adaptivity criterion for each variable has its own subsection - for (const auto &[index, variable] : var_attributes) - { - std::string subsection_text = "Refinement criterion: "; - subsection_text.append(variable.name); - - parameter_handler.enter_subsection(subsection_text); - { - const std::string crit_type_string = parameter_handler.get("Criterion type"); - if (!crit_type_string.empty()) - { - RefinementCriterion new_criterion; - new_criterion.variable_index = index; - new_criterion.variable_name = variable.name; - if (boost::iequals(crit_type_string, "VALUE")) - { - new_criterion.criterion_type = criterion_value; - new_criterion.value_lower_bound = - parameter_handler.get_double("Value lower bound"); - new_criterion.value_upper_bound = - parameter_handler.get_double("Value upper bound"); - - // Check to make sure that the upper bound is greater than or - // equal to the lower bound - if (new_criterion.value_upper_bound < new_criterion.value_lower_bound) - { - std::cerr << "PRISMS-PF Error: The upper bound for " - "refinement for variable " - << new_criterion.variable_name - << " is less than the lower bound. Please " - "correct this in the parameters file.\n"; - } - } - else if (boost::iequals(crit_type_string, "GRADIENT")) - { - new_criterion.criterion_type = criterion_gradient; - new_criterion.gradient_lower_bound = - parameter_handler.get_double("Gradient magnitude lower bound"); - } - else if (boost::iequals(crit_type_string, "VALUE_AND_GRADIENT")) - { - new_criterion.criterion_type = criterion_value | criterion_gradient; - new_criterion.value_lower_bound = - parameter_handler.get_double("Value lower bound"); - new_criterion.value_upper_bound = - parameter_handler.get_double("Value upper bound"); - new_criterion.gradient_lower_bound = - parameter_handler.get_double("Gradient magnitude lower bound"); - - // Check to make sure that the upper bound is greater than or - // equal to the lower bound - if (new_criterion.value_upper_bound < new_criterion.value_lower_bound) - { - std::cerr << "PRISMS-PF Error: The upper bound for " - "refinement for variable " - << new_criterion.variable_name - << " is less than the lower bound. Please " - "correct this in the parameters file.\n"; - } - } - else - { - std::cerr << "PRISMS-PF Error: The refinement criteria type " - "found in the parameters file, " - << crit_type_string - << ", is not an allowed type. The allowed types are " - "VALUE, GRADIENT, VALUE_AND_GRADIENT\n"; - abort(); - } - refinement_criteria.push_back(new_criterion); - } - } - parameter_handler.leave_subsection(); - } -} - -template -void -userInputParameters::assign_temporal_discretization_parameters( - dealii::ParameterHandler ¶meter_handler) -{ - dtValue = parameter_handler.get_double("Time step"); - const int totalIncrements_temp = - static_cast(parameter_handler.get_integer("Number of time steps")); - finalTime = parameter_handler.get_double("Simulation end time"); - - // If all of the variables are ELLIPTIC, then totalIncrements should be 1 and - // finalTime should be 0 - bool only_time_independent_pdes = true; - for (const auto &[index, variable] : var_attributes) - { - if (variable.eq_type == EXPLICIT_TIME_DEPENDENT || - variable.eq_type == IMPLICIT_TIME_DEPENDENT) - { - only_time_independent_pdes = false; - break; - } - } - - // Determine the maximum number of time steps - if (only_time_independent_pdes) - { - totalIncrements = 1; - finalTime = 0.0; - } - else - { - if ((totalIncrements_temp >= 0) && (finalTime >= 0.0)) - { - if (std::ceil(finalTime / dtValue) > totalIncrements_temp) - { - totalIncrements = totalIncrements_temp; - finalTime = totalIncrements * dtValue; - } - else - { - totalIncrements = std::ceil(finalTime / dtValue); - } - } - else if ((totalIncrements_temp >= 0) && (finalTime < 0.0)) - { - totalIncrements = totalIncrements_temp; - finalTime = totalIncrements * dtValue; - } - else if ((totalIncrements_temp < 0) && (finalTime >= 0.0)) - { - totalIncrements = std::ceil(finalTime / dtValue); - } - else - { - // Should change to an exception - std::cerr << "Invalid selections for the final time and the number " - "of increments. At least one should be given in the " - "input file and should be positive.\n"; - std::cout << finalTime << " " << totalIncrements_temp << "\n"; - abort(); - } - } -} - -template -void -userInputParameters::assign_linear_solve_parameters( - dealii::ParameterHandler ¶meter_handler) -{ - for (const auto &[index, variable] : var_attributes) - { - if (variable.eq_type == TIME_INDEPENDENT || - variable.eq_type == IMPLICIT_TIME_DEPENDENT) - { - std::string subsection_text = "Linear solver parameters: "; - subsection_text.append(variable.name); - - parameter_handler.enter_subsection(subsection_text); - { - // Set the tolerance type - SolverToleranceType temp_type = ABSOLUTE_RESIDUAL; - const std::string type_string = parameter_handler.get("Tolerance type"); - if (boost::iequals(type_string, "ABSOLUTE_RESIDUAL")) - { - temp_type = ABSOLUTE_RESIDUAL; - } - else if (boost::iequals(type_string, "RELATIVE_RESIDUAL_CHANGE")) - { - temp_type = RELATIVE_RESIDUAL_CHANGE; - } - else if (boost::iequals(type_string, "ABSOLUTE_SOLUTION_CHANGE")) - { - temp_type = ABSOLUTE_SOLUTION_CHANGE; - AssertThrow(false, - FeatureNotImplemented( - "Linear solver tolerance ABSOLUTE_SOLUTION_CHANGE")); - } - else - { - std::cerr << "PRISMS-PF Error: Linear solver tolerance type " - << type_string - << " is not one of the allowed values (ABSOLUTE_RESIDUAL, " - "RELATIVE_RESIDUAL_CHANGE, ABSOLUTE_SOLUTION_CHANGE)\n"; - abort(); - } - - // Set the tolerance value - const double temp_value = parameter_handler.get_double("Tolerance value"); - - // Set the maximum number of iterations - const unsigned int temp_max_iterations = - parameter_handler.get_integer("Maximum linear solver iterations"); - - linear_solver_parameters.loadParameters(index, - temp_type, - temp_value, - temp_max_iterations); - } - parameter_handler.leave_subsection(); - } - } -} - -template -void -userInputParameters::assign_nonlinear_solve_parameters( - dealii::ParameterHandler ¶meter_handler) -{ - nonlinear_solver_parameters.setMaxIterations( - parameter_handler.get_integer("Maximum nonlinear solver iterations")); - - for (const auto &[index, variable] : var_attributes) - { - if (variable.is_nonlinear) - { - std::string subsection_text = "Nonlinear solver parameters: "; - subsection_text.append(variable.name); - - parameter_handler.enter_subsection(subsection_text); - { - // Set the tolerance type - SolverToleranceType temp_type = ABSOLUTE_RESIDUAL; - const std::string type_string = parameter_handler.get("Tolerance type"); - if (boost::iequals(type_string, "ABSOLUTE_RESIDUAL")) - { - temp_type = ABSOLUTE_RESIDUAL; - } - else if (boost::iequals(type_string, "RELATIVE_RESIDUAL_CHANGE")) - { - temp_type = RELATIVE_RESIDUAL_CHANGE; - } - else if (boost::iequals(type_string, "ABSOLUTE_SOLUTION_CHANGE")) - { - temp_type = ABSOLUTE_SOLUTION_CHANGE; - } - else - { - std::cerr << "PRISMS-PF Error: Nonlinear solver tolerance type " - << type_string - << " is not one of the allowed values (ABSOLUTE_RESIDUAL, " - "RELATIVE_RESIDUAL_CHANGE, ABSOLUTE_SOLUTION_CHANGE)\n"; - abort(); - } - - // Set the tolerance value - const double temp_value = parameter_handler.get_double("Tolerance value"); - - // Set the backtrace damping flag - const bool temp_backtrack_damping = - parameter_handler.get_bool("Use backtracking line search damping"); - - // Set the backtracking step size modifier - const double temp_step_modifier = - parameter_handler.get_double("Backtracking step size modifier"); - - // Set the constant that determines how much the residual must - // decrease to be accepted as sufficient - const double temp_residual_decrease_coeff = - parameter_handler.get_double("Backtracking residual decrease coefficient"); - - // Set the default damping coefficient (used if backtracking isn't - // used) - const double temp_damping_coefficient = - parameter_handler.get_double("Constant damping value"); - - // Set whether to use the solution of Laplace's equation instead of - // the IC in ICs_and_BCs.h as the initial guess for nonlinear, time - // independent equations - bool temp_laplace_for_initial_guess = false; - if (variable.eq_type == TIME_INDEPENDENT) - { - temp_laplace_for_initial_guess = parameter_handler.get_bool( - "Use Laplace's equation to determine the initial guess"); - } - else - { - if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - std::cout << "PRISMS-PF Warning: Laplace's equation is only used " - "to generate the initial guess for time independent " - "equations. The equation for variable " - << variable.name - << " is not a time independent equation. No initial " - "guess is needed for this equation.\n"; - } - } - - nonlinear_solver_parameters.loadParameters(index, - temp_type, - temp_value, - temp_backtrack_damping, - temp_step_modifier, - temp_residual_decrease_coeff, - temp_damping_coefficient, - temp_laplace_for_initial_guess); - } - parameter_handler.leave_subsection(); - } - } - - // Set the max number of nonlinear iterations - bool any_nonlinear = false; - for (const auto &[index, variable] : var_attributes) - { - any_nonlinear |= variable.is_nonlinear; - } - if (!any_nonlinear) - { - nonlinear_solver_parameters.setMaxIterations(0); - } -} - -template -void -userInputParameters::assign_output_parameters( - dealii::ParameterHandler ¶meter_handler) -{ - const std::string output_condition = parameter_handler.get("Output condition"); - const unsigned int num_outputs = parameter_handler.get_integer("Number of outputs"); - const std::vector user_given_time_step_list_temp = - dealii::Utilities::string_to_int(dealii::Utilities::split_string_list( - parameter_handler.get("List of time steps to output"))); - std::vector user_given_time_step_list; - user_given_time_step_list.reserve(user_given_time_step_list_temp.size()); - for (const auto &time_step : user_given_time_step_list_temp) - { - user_given_time_step_list.push_back(time_step); - } - - skip_print_steps = parameter_handler.get_integer("Skip print steps"); - output_file_type = parameter_handler.get("Output file type"); - output_file_name = parameter_handler.get("Output file name (base)"); - - output_vtu_per_process = - parameter_handler.get_bool("Output separate files per process"); - if ((output_file_type == "vtk") && (!output_vtu_per_process)) - { - output_vtu_per_process = true; - if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - std::cout << "PRISMS-PF Warning: 'Output file type' given as 'vtk' and " - "'Output separate files per process' given as 'false'. Shared " - "output files are not supported for the vtk output format. " - "Separate files per process will be created.\n"; - } - } - - print_timing_with_output = - parameter_handler.get_bool("Print timing information with output"); - - // Field variable definitions - - // Use these inputs to create a list of time steps where the code should - // output, stored in the member - outputTimeStepList = - setTimeStepList(output_condition, num_outputs, user_given_time_step_list); - - // Parameters for checkpoint/restart - resume_from_checkpoint = parameter_handler.get_bool("Load from a checkpoint"); - const std::string checkpoint_condition = parameter_handler.get("Checkpoint condition"); - const unsigned int num_checkpoints = - parameter_handler.get_integer("Number of checkpoints"); - - const std::vector user_given_checkpoint_time_step_list_temp = - dealii::Utilities::string_to_int(dealii::Utilities::split_string_list( - parameter_handler.get("List of time steps to save checkpoints"))); - std::vector user_given_checkpoint_time_step_list; - user_given_checkpoint_time_step_list.reserve( - user_given_checkpoint_time_step_list_temp.size()); - for (const auto &checkpoint_step : user_given_checkpoint_time_step_list_temp) - { - user_given_checkpoint_time_step_list.push_back(checkpoint_step); - } - checkpointTimeStepList = setTimeStepList(checkpoint_condition, - num_checkpoints, - user_given_checkpoint_time_step_list); -} - -template -void -userInputParameters::assign_load_initial_condition_parameters( - dealii::ParameterHandler ¶meter_handler) -{ // Variables for loading in PField ICs - std::vector load_ICs_temp = dealii::Utilities::split_string_list( - parameter_handler.get("Load initial conditions")); - std::vector load_parallel_file_temp = - dealii::Utilities::split_string_list(parameter_handler.get("Load parallel file")); - - if (boost::iequals(load_ICs_temp.at(0), "void")) - { - for (unsigned int var = 0; var < var_attributes.size(); var++) - { - load_ICs.push_back(false); - load_parallel_file.push_back(false); - } - } - else - { - for (unsigned int var = 0; var < var_attributes.size(); var++) - { - if (boost::iequals(load_ICs_temp.at(var), "true")) - { - load_ICs.push_back(true); - } - else - { - load_ICs.push_back(false); - } - if (boost::iequals(load_parallel_file_temp.at(var), "true")) - { - load_parallel_file.push_back(true); - } - else - { - load_parallel_file.push_back(false); - } - } - } - - load_file_name = - dealii::Utilities::split_string_list(parameter_handler.get("File names")); - load_field_name = dealii::Utilities::split_string_list( - parameter_handler.get("Variable names in the files")); -} - -template -void -userInputParameters::assign_nucleation_parameters( - dealii::ParameterHandler ¶meter_handler) -{ - for (const auto &[index, variable] : var_attributes) - { - if (variable.nucleating_variable) - { - std::string nucleation_text = "Nucleation parameters: "; - nucleation_text.append(variable.name); - - parameter_handler.enter_subsection(nucleation_text); - { - const unsigned int var_index = index; - const std::vector semiaxes = - dealii::Utilities::string_to_double(dealii::Utilities::split_string_list( - parameter_handler.get("Nucleus semiaxes (x, y, z)"))); - const std::vector ellipsoid_rotation = - dealii::Utilities::string_to_double(dealii::Utilities::split_string_list( - parameter_handler.get("Nucleus rotation in degrees (x, y, z)"))); - const std::vector freeze_semiaxes = - dealii::Utilities::string_to_double(dealii::Utilities::split_string_list( - parameter_handler.get("Freeze zone semiaxes (x, y, z)"))); - const double hold_time = - parameter_handler.get_double("Freeze time following nucleation"); - const double no_nucleation_border_thickness = - parameter_handler.get_double("Nucleation-free border thickness"); - - const nucleationParameters temp(var_index, - semiaxes, - freeze_semiaxes, - ellipsoid_rotation, - hold_time, - no_nucleation_border_thickness); - nucleation_parameters_list.push_back(temp); - - // Validate nucleation input - if (semiaxes.size() < dim || semiaxes.size() > 3) - { - std::cerr << "PRISMS-PF Error: The number of nucleus semiaxes given in " - "the 'parameters.in' file must be at least the number of " - "dimensions and no more than 3.\n"; - abort(); - } - if (freeze_semiaxes.size() < dim || freeze_semiaxes.size() > 3) - { - std::cerr << "PRISMS-PF Error: The number of nucleation freeze zone " - "semiaxes given in the 'parameters.in' file must be at " - "least the number of dimensions and no more than 3.\n"; - abort(); - } - if (ellipsoid_rotation.size() != 3) - { - std::cerr << "PRISMS-PF Error: Exactly three nucleus rotation " - "angles must be given in the 'parameters.in' file.\n"; - abort(); - } - } - parameter_handler.leave_subsection(); - } - } - for (unsigned int i = 0; i < nucleation_parameters_list.size(); i++) - { - nucleation_parameters_list_index[nucleation_parameters_list.at(i).var_index] = i; - } - - if (parameter_handler.get("Minimum allowed distance between nuclei") != "-1") - { - min_distance_between_nuclei = - parameter_handler.get_double("Minimum allowed distance between nuclei"); - } - else if (nucleation_parameters_list.size() > 1) - { - min_distance_between_nuclei = - 2.0 * (*(max_element(nucleation_parameters_list[0].semiaxes.begin(), - nucleation_parameters_list[0].semiaxes.end()))); - } - evolution_before_nucleation = - parameter_handler.get_bool("Enable evolution before nucleation"); - // Implement multiple order parameter nucleation later - // multiple_nuclei_per_order_parameter = parameter_handler.get_bool("Allow - // multiple nuclei per order parameter"); - nucleation_order_parameter_cutoff = - parameter_handler.get_double("Order parameter cutoff value"); - steps_between_nucleation_attempts = - parameter_handler.get_integer("Time steps between nucleation attempts"); - nucleation_start_time = parameter_handler.get_double("Nucleation start time"); - nucleation_end_time = parameter_handler.get_double("Nucleation end time"); -} - -template -void -userInputParameters::assign_grain_parameters( - dealii::ParameterHandler ¶meter_handler) -{ // Load the grain remapping parameters - grain_remapping_activated = parameter_handler.get_bool("Activate grain reassignment"); - - skip_grain_reassignment_steps = - parameter_handler.get_integer("Time steps between grain reassignments"); - - order_parameter_threshold = - parameter_handler.get_double("Order parameter cutoff for grain identification"); - - buffer_between_grains = - parameter_handler.get_double("Buffer between grains before reassignment"); - if (buffer_between_grains < 0.0 && grain_remapping_activated) - { - std::cerr << "PRISMS-PF Error: If grain reassignment is activated, a " - "non-negative buffer distance must be given. See the 'Buffer " - "between grains before reassignment' entry in parameters.in.\n"; - abort(); - } - - const std::vector variables_for_remapping_str = - dealii::Utilities::split_string_list( - parameter_handler.get("Order parameter fields for grain reassignment")); - for (const auto &field : variables_for_remapping_str) - { - bool field_found = false; - for (const auto &[index, variable] : var_attributes) - { - if (boost::iequals(field, variable.name)) - { - variables_for_remapping.push_back(index); - field_found = true; - break; - } - } - if (!field_found && grain_remapping_activated) - { - std::cerr << "PRISMS-PF Error: Entries in the list of order " - "parameter fields used for grain reassignment must " - "match the variable names in equations.h.\n"; - std::cerr << field << "\n"; - abort(); - } - } - - load_grain_structure = parameter_handler.get_bool("Load grain structure"); - load_vtk_file_type = - parameter_handler.get("vtk file type"); // assign the vtk file type and getting it - // ready to send to initialconditions.cc - grain_structure_filename = parameter_handler.get("Grain structure filename"); - grain_structure_variable_name = parameter_handler.get("Grain structure variable name"); - num_grain_smoothing_cycles = parameter_handler.get_integer( - "Number of smoothing cycles after grain structure loading"); - min_radius_for_loading_grains = - parameter_handler.get_double("Minimum radius for loaded grains"); -} - -template -void -userInputParameters::assign_boundary_condition_parameters( - dealii::ParameterHandler ¶meter_handler) -{ - // Load the boundary condition variables into list of BCs (where each element - // of the vector is one component of one variable) - std::vector list_of_BCs; - for (const auto &[index, variable] : var_attributes) - { - if (variable.var_type == SCALAR) - { - std::string bc_text = "Boundary condition for variable "; - bc_text.append(variable.name); - list_of_BCs.push_back(parameter_handler.get(bc_text)); - } - else - { - std::string bc_text = "Boundary condition for variable "; - bc_text.append(variable.name); - bc_text.append(", x component"); - list_of_BCs.push_back(parameter_handler.get(bc_text)); - - bc_text = "Boundary condition for variable "; - bc_text.append(variable.name); - bc_text.append(", y component"); - list_of_BCs.push_back(parameter_handler.get(bc_text)); - - if (dim > 2) - { - bc_text = "Boundary condition for variable "; - bc_text.append(variable.name); - bc_text.append(", z component"); - list_of_BCs.push_back(parameter_handler.get(bc_text)); - } - } - } - - /*---------------------- - | Pinning point - -----------------------*/ - for (const auto &[index, variable] : var_attributes) - { - std::string pinning_text = "Pinning point: "; - pinning_text.append(variable.name); - parameter_handler.enter_subsection(pinning_text); - - // Skip if the default - if (parameter_handler.get_double("x") == -1.0) - { - parameter_handler.leave_subsection(); - continue; - } - - // Otherwise, fill out point - if (dim == 2) - { - pinned_point[index] = dealii::Point(parameter_handler.get_double("x"), - parameter_handler.get_double("y")); - } - else - { - pinned_point[index] = dealii::Point(parameter_handler.get_double("x"), - parameter_handler.get_double("y"), - parameter_handler.get_double("z")); - } - parameter_handler.leave_subsection(); - } - - // Load the BC information from the strings into a varBCs object - load_BC_list(list_of_BCs); -} - -template -void -userInputParameters::assign_boundary_conditions( - std::vector &boundary_condition_list, - varBCs &boundary_condition) -{ - // If there is only one boundary condition, copy it to have 2*dim copies. - if (boundary_condition_list.size() == 1) - { - boundary_condition_list.resize(static_cast(2 * dim), - boundary_condition_list[0]); - } - - // Assign the boundary condition into the varBCs object. - for (unsigned int j = 0; j < (2 * dim); j++) - { - if (boost::iequals(boundary_condition_list[j], "NATURAL")) - { - boundary_condition.var_BC_type.push_back(NATURAL); - boundary_condition.var_BC_val.push_back(0.0); - } - else if (boost::iequals(boundary_condition_list[j], "PERIODIC")) - { - boundary_condition.var_BC_type.push_back(PERIODIC); - boundary_condition.var_BC_val.push_back(0.0); - } - else if (boost::iequals(boundary_condition_list[j], "NON_UNIFORM_DIRICHLET")) - { - boundary_condition.var_BC_type.push_back(NON_UNIFORM_DIRICHLET); - boundary_condition.var_BC_val.push_back(0.0); - } - else if (boost::iequals(boundary_condition_list[j].substr(0, 9), "DIRICHLET")) - { - boundary_condition.var_BC_type.push_back(DIRICHLET); - std::string dirichlet_val = - boundary_condition_list[j].substr(10, boundary_condition_list[j].size()); - dirichlet_val = dealii::Utilities::trim(dirichlet_val); - boundary_condition.var_BC_val.push_back( - dealii::Utilities::string_to_double(dirichlet_val)); - } - else if (boost::iequals(boundary_condition_list[j].substr(0, 7), "NEUMANN")) - { - boundary_condition.var_BC_type.push_back(NEUMANN); - std::string neumann_val = - boundary_condition_list[j].substr(8, boundary_condition_list[j].size()); - neumann_val = dealii::Utilities::trim(neumann_val); - boundary_condition.var_BC_val.push_back( - dealii::Utilities::string_to_double(neumann_val)); - } - else - { - std::cout << boundary_condition_list[j].substr(0, 8) << "\n"; - std::cout << "Error: Boundary conditions specified improperly.\n"; - abort(); - } - - // If periodic BCs are used, ensure they are applied on both sides of - // domain - if (j % 2 == 0) - { - AssertThrow(boost::iequals(boundary_condition_list[j], "PERIODIC") == - boost::iequals(boundary_condition_list[j + 1], "PERIODIC"), - dealii::ExcMessage( - std::string("Periodic boundary condition must be " - "specified on both sides of domain"))); - } - } -} - -template -void -userInputParameters::load_BC_list(const std::vector &list_of_BCs) -{ - // Loop over the list of boundary conditions specified in parameters - // and provided in the input list_of_BCs. Process the BCs and place - // them into the vector BC_list - std::vector split_boundary_conditions; - for (const auto &boundary_condition : list_of_BCs) - { - // Ensure all variables have BCs specified in parameters.prm - AssertThrow(!boundary_condition.empty(), - dealii::ExcMessage(std::string("Boundary condition not specified."))); - - // Create object to store BCs - varBCs newBC; - - // Split string of boundary conditions - split_boundary_conditions = - dealii::Utilities::split_string_list(boundary_condition); - - // Assign the boundaries into the object - assign_boundary_conditions(split_boundary_conditions, newBC); - - // Append BCs for current field to total list - BC_list.push_back(newBC); - } -} - -template -std::vector -userInputParameters::setTimeStepList( - const std::string &outputSpacingType, - unsigned int numberOfOutputs, - const std::vector &userGivenTimeStepList) -{ - // Initialize timestep list - std::vector timeStepList; - - // The number of outputs cannot be greater than the number increments - numberOfOutputs = std::min(numberOfOutputs, totalIncrements); - - // Prevent divide by zero in subsequent output types by returning the a vector where the - // only entry is one greater than the number of increments. This way, we effectively - // have no outputs. While this condition can be ignored for the LIST type, the user - // should just ignore the parameter `set Number of outputs` and use the default value - // of 10. - if (numberOfOutputs == 0) - { - timeStepList.push_back(totalIncrements + 1); - return timeStepList; - } - - // Set output list for all the output list types - if (outputSpacingType == "LIST") - { - timeStepList = userGivenTimeStepList; - } - else if (outputSpacingType == "EQUAL_SPACING") - { - for (unsigned int iter = 0; iter <= totalIncrements; - iter += totalIncrements / numberOfOutputs) - { - timeStepList.push_back(iter); - } - } - else if (outputSpacingType == "LOG_SPACING") - { - timeStepList.push_back(0); - for (unsigned int output = 1; output <= numberOfOutputs; output++) - { - timeStepList.push_back(round(std::pow(static_cast(totalIncrements), - static_cast(output) / - static_cast(numberOfOutputs)))); - } - } - else if (outputSpacingType == "N_PER_DECADE") - { - AssertThrow(totalIncrements > 1, - dealii::ExcMessage( - std::string("PRISMS-PF Error: For n per decaded spaced outputs, " - "the number of increments must be greater than 1."))); - - timeStepList.push_back(0); - timeStepList.push_back(1); - for (unsigned int iter = 2; iter <= totalIncrements; iter++) - { - const unsigned int decade = std::ceil(std::log10(iter)); - const auto step_size = - static_cast(std::pow(10, decade) / numberOfOutputs); - if (iter % step_size == 0) - { - timeStepList.push_back(iter); - } - } - } - else - { - AssertThrow(false, - dealii::ExcMessage( - std::string("PRISMS-PF Error: Invalid output spacing type."))); - } - - return timeStepList; -} - -template -unsigned int -userInputParameters::compute_tensor_parentheses( - const unsigned int n_elements, - const std::vector &tensor_elements) -{ - unsigned int open_parentheses = 0; - unsigned int close_parentheses = 0; - - for (unsigned int element = 0; element < n_elements; element++) - { - for (const char c : tensor_elements.at(element)) - { - if (c == '(') - { - ++open_parentheses; - } - else if (c == ')') - { - ++close_parentheses; - } - } - } - - if (open_parentheses != close_parentheses) - { - std::cerr << "PRISMS-PF ERROR: User-defined elastic constant " - "list does not have the same number of open and " - "close parentheses.\n"; - abort(); - } - - return open_parentheses; -} - -template -void -userInputParameters::remove_parentheses(std::vector &tensor_elements) -{ - for (std::string &element : tensor_elements) - { - element.erase(std::remove(element.begin(), element.end(), '('), element.end()); - element.erase(std::remove(element.begin(), element.end(), ')'), element.end()); - } -} - -template -dealii::Tensor<1, dim> -userInputParameters::compute_rank_1_tensor_constant( - const unsigned int n_elements, - std::vector tensor_elements) -{ - AssertThrow(n_elements > 1 && n_elements < 4, - dealii::ExcMessage("PRISMS-PF Error: The columns in user-defined constant " - "tensors cannot be longer than 3 elements (internally " - "truncated to the number of dimensions).")); - - dealii::Tensor<1, dim> temp; - for (unsigned int i = 0; i < dim; i++) - { - temp[i] = dealii::Utilities::string_to_double(tensor_elements.at(i)); - } - - return temp; -} - -template -dealii::Tensor<2, dim> -userInputParameters::compute_rank_2_tensor_constant( - const unsigned int n_elements, - std::vector tensor_elements) -{ - unsigned int row_length = 0; - if (n_elements == 4) - { - AssertThrow(dim < 3, - dealii::ExcMessage( - "PRISMS-PF ERROR: User-defined constant tensor does not have " - "enough elements. For 3D calculations matrices must be 3x3.")); - - row_length = 2; - } - else if (n_elements == 9) - { - row_length = 3; - } - else - { - AssertThrow(false, - dealii::ExcMessage("PRISMS-PF ERROR: User-defined constant tensor does " - "not have the correct number of elements, matrices " - "must be 2x2 or 3x3.")); - } - - dealii::Tensor<2, dim> temp; - for (unsigned int i = 0; i < dim; i++) - { - for (unsigned int j = 0; j < dim; j++) - { - temp[i][j] = - dealii::Utilities::string_to_double(tensor_elements.at(i * row_length + j)); - } - } - - return temp; -} - -template -InputVariant -userInputParameters::construct_user_constant( - std::vector &model_constants_strings) -{ - // Ensure that the input includes a value and a type - AssertThrow(model_constants_strings.size() > 1, - dealii::ExcMessage("PRISMS-PF Error: At least two fields are required for " - "user-defined variables (value and type).")); - - std::vector model_constants_type_strings = - dealii::Utilities::split_string_list(model_constants_strings.at( - model_constants_strings.size() - 1), - ' '); - - if (model_constants_strings.size() == 2) - { - return primitive_model_constant(model_constants_strings); - } - else - { - if (boost::iequals(model_constants_type_strings.at(0), "tensor")) - { - const unsigned int n_elements = model_constants_strings.size() - 1; - - const unsigned int open_parentheses = - compute_tensor_parentheses(n_elements, model_constants_strings); - remove_parentheses(model_constants_strings); - - // Rank 1 tensor - if (open_parentheses < 3) - { - return compute_rank_1_tensor_constant(n_elements, model_constants_strings); - } - // Rank 2 tensor - else if (open_parentheses < 5) - { - return compute_rank_2_tensor_constant(n_elements, model_constants_strings); - } - } - else if (boost::iequals(model_constants_type_strings.at(1), "elastic") && - boost::iequals(model_constants_type_strings.at(2), "constants")) - { - const unsigned int n_elements = model_constants_strings.size() - 1; - - remove_parentheses(model_constants_strings); - - // Load in the elastic constants as a vector - std::vector temp_elastic_constants; - for (unsigned int i = 0; i < n_elements; i++) - { - temp_elastic_constants.push_back( - dealii::Utilities::string_to_double(model_constants_strings.at(i))); - } - - const std::string elastic_const_symmetry = model_constants_type_strings.at(0); - dealii::Tensor<2, 2 *dim - 1 + dim / 3> temp = - get_Cij_tensor(temp_elastic_constants, elastic_const_symmetry); - return temp; - } - else - { - AssertThrow(false, - dealii::ExcMessage( - "PRISMS-PF ERROR: Only user-defined constant tensors may " - "have multiple elements.")); - } - return 0; - } -} - -template -InputVariant -userInputParameters::primitive_model_constant( - std::vector &model_constants_strings) -{ - std::vector model_constants_type_strings = - dealii::Utilities::split_string_list(model_constants_strings.at( - model_constants_strings.size() - 1), - ' '); - - if (boost::iequals(model_constants_type_strings.at(0), "double")) - { - return dealii::Utilities::string_to_double(model_constants_strings.at(0)); - } - else if (boost::iequals(model_constants_type_strings.at(0), "int")) - { - return dealii::Utilities::string_to_int(model_constants_strings.at(0)); - } - else if (boost::iequals(model_constants_type_strings.at(0), "bool")) - { - bool temp = boost::iequals(model_constants_strings.at(0), "true"); - return temp; - } - else - { - AssertThrow(false, - dealii::ExcMessage( - "PRISMS-PF Error: The type for user-defined variables must be " - "`double`, `int`, `bool`, `tensor`, or `elastic constants`.")); - return 0; - } -} - -template -void -userInputParameters::load_model_constants( - inputFileReader &input_file_reader, - dealii::ParameterHandler ¶meter_handler) -{ - for (const std::string &constant_name : input_file_reader.model_constant_names) - { - std::string constants_text = "Model constant "; - constants_text.append(constant_name); - - std::vector model_constants_strings = - dealii::Utilities::split_string_list(parameter_handler.get(constants_text)); - - model_constants[constant_name] = construct_user_constant(model_constants_strings); - } -} - -template -dealii::Tensor<2, 2 * dim - 1 + dim / 3> -userInputParameters::get_Cij_tensor(std::vector elastic_constants, - const std::string &elastic_const_symmetry) const -{ - // First set the material model - elasticityModel mat_model = ISOTROPIC; - if (elastic_const_symmetry == "isotropic") - { - mat_model = ISOTROPIC; - } - else if (elastic_const_symmetry == "transverse") - { - mat_model = TRANSVERSE; - } - else if (elastic_const_symmetry == "orthotropic") - { - mat_model = ORTHOTROPIC; - } - else if (elastic_const_symmetry == "anisotropic") - { - mat_model = ANISOTROPIC; - } - else - { - // Should change to an exception - std::cerr << "Elastic material model is invalid, please use isotropic, " - "transverse, orthotropic, or anisotropic\n"; - } - - // If the material model is anisotropic for a 2D calculation but the elastic - // constants are given for a 3D calculation, change the elastic constant - // vector to the 2D form - if ((mat_model == ANISOTROPIC) && (dim == 2) && elastic_constants.size() == 21) - { - std::vector elastic_constants_temp = elastic_constants; - elastic_constants.clear(); - const std::vector indices_2D = {0, 1, 5, 6, 10, 14}; - for (const auto &index : indices_2D) - { - elastic_constants.push_back(elastic_constants_temp.at(index)); - } - } - - dealii::ConditionalOStream pcout(std::cout, - dealii::Utilities::MPI::this_mpi_process( - MPI_COMM_WORLD) == 0); - - return getCIJMatrix(mat_model, elastic_constants, pcout); -} - -template -dealii::Tensor<2, 2 * dim - 1 + dim / 3> -userInputParameters::getCIJMatrix(const elasticityModel model, - const std::vector &constants, - dealii::ConditionalOStream &pcout) const -{ - // CIJ.fill(0.0); - dealii::Tensor<2, 2 * dim - 1 + dim / 3> CIJ; - - pcout << "Reading material model:"; - switch (dim) - { - case 1: - { - pcout << " 1D "; - // 1D models - switch (model) - { - case ISOTROPIC: - { - pcout << " ISOTROPIC \n"; - CIJ[0][0] = constants[0]; - break; - } - default: - { - std::cout << "\nelasticityModels: Supported models in 1D - " - "ISOTROPIC\n"; - std::cout << "See /src/elasticityModels.h\n"; - exit(-1); - } - } - break; - } - case 2: - { - pcout << " 2D "; - // 2D models - switch (model) - { - case ISOTROPIC: - { - pcout << " ISOTROPIC \n"; - const double E = constants[0]; - const double nu = constants[1]; - const double mu = E / (2 * (1 + nu)); - const double lambda = nu * E / ((1 + nu) * (1 - 2 * nu)); - CIJ[0][0] = lambda + 2 * mu; - CIJ[1][1] = lambda + 2 * mu; - CIJ[2][2] = mu; - CIJ[0][1] = CIJ[1][0] = lambda; - break; - } - case ANISOTROPIC: - { - pcout << " ANISOTROPIC \n"; - CIJ[0][0] = constants[0]; // C11 - CIJ[1][1] = constants[1]; // C22 - CIJ[2][2] = constants[2]; // C33 - CIJ[0][1] = CIJ[1][0] = constants[3]; // C12 - CIJ[0][2] = CIJ[2][0] = constants[4]; // C13 - CIJ[1][2] = CIJ[2][1] = constants[5]; // C23 - break; - } - default: - { - std::cout << "\nelasticityModels: Supported models in 2D - " - "ISOTROPIC/ANISOTROPIC\n"; - std::cout << "See /src/elasticityModels.h\n"; - exit(-1); - } - } - break; - } - case 3: - { - pcout << " 3D "; - // 3D models - switch (model) - { - case ISOTROPIC: - { - pcout << " ISOTROPIC \n"; - const double E = constants[0]; - const double nu = constants[1]; - const double mu = E / (2 * (1 + nu)); - const double lambda = nu * E / ((1 + nu) * (1 - 2 * nu)); - CIJ[0][0] = lambda + 2 * mu; - CIJ[1][1] = lambda + 2 * mu; - CIJ[2][2] = lambda + 2 * mu; - CIJ[3][3] = mu; - CIJ[4][4] = mu; - CIJ[5][5] = mu; - CIJ[0][1] = CIJ[1][0] = lambda; - CIJ[0][2] = CIJ[2][0] = lambda; - CIJ[1][2] = CIJ[2][1] = lambda; - break; - } - case TRANSVERSE: - { - pcout << " TRANSVERSE \n"; - CIJ[0][0] = constants[0]; // C11 - CIJ[1][1] = constants[0]; // C11 - CIJ[2][2] = constants[1]; // C33 - CIJ[3][3] = constants[2]; // C44 - CIJ[4][4] = constants[2]; // C44 - CIJ[5][5] = (constants[0] - constants[3]) / 2.0; //(C11-C12)/2 - CIJ[0][1] = CIJ[1][0] = constants[3]; // C12 - CIJ[0][2] = CIJ[2][0] = constants[4]; // C13 - CIJ[1][2] = CIJ[2][1] = constants[4]; // C13 - break; - } - case ORTHOTROPIC: - { - pcout << " ORTHOTROPIC \n"; - CIJ[0][0] = constants[0]; // C11 - CIJ[1][1] = constants[1]; // C22 - CIJ[2][2] = constants[2]; // C33 - CIJ[3][3] = constants[3]; // C44 - CIJ[4][4] = constants[4]; // C55 - CIJ[5][5] = constants[5]; // C66 - CIJ[0][1] = CIJ[1][0] = constants[6]; // C12 - CIJ[0][2] = CIJ[2][0] = constants[7]; // C13 - CIJ[1][2] = CIJ[2][1] = constants[8]; // C23 - break; - } - case ANISOTROPIC: - { - pcout << " ANISOTROPIC \n"; - CIJ[0][0] = constants[0]; // C11 - CIJ[1][1] = constants[1]; // C22 - CIJ[2][2] = constants[2]; // C33 - CIJ[3][3] = constants[3]; // C44 - CIJ[4][4] = constants[4]; // C55 - CIJ[5][5] = constants[5]; // C66 - CIJ[0][1] = CIJ[1][0] = constants[6]; // C12 - CIJ[0][2] = CIJ[2][0] = constants[7]; // C13 - CIJ[0][3] = CIJ[3][0] = constants[8]; // C14 - CIJ[0][4] = CIJ[4][0] = constants[9]; // C15 - CIJ[0][5] = CIJ[5][0] = constants[10]; // C16 - CIJ[1][2] = CIJ[2][1] = constants[11]; // C23 - CIJ[1][3] = CIJ[3][1] = constants[12]; // C24 - CIJ[1][4] = CIJ[4][1] = constants[13]; // C25 - CIJ[1][5] = CIJ[5][1] = constants[14]; // C26 - CIJ[2][3] = CIJ[3][2] = constants[15]; // C34 - CIJ[2][4] = CIJ[4][2] = constants[16]; // C35 - CIJ[2][5] = CIJ[5][2] = constants[17]; // C36 - CIJ[3][4] = CIJ[4][3] = constants[18]; // C45 - CIJ[3][5] = CIJ[5][3] = constants[19]; // C46 - CIJ[4][5] = CIJ[5][4] = constants[20]; // C56 - break; - } - default: - { - std::cout << "\nelasticityModels: Supported models in 3D - " - "ISOTROPIC/TRANSVERSE/ORTHOTROPIC/ANISOTROPIC\n"; - std::cout << "See /src/elasticityModels.h\n"; - exit(-1); - } - } - break; - } - default: - { - std::cout << "\nelasticityModels: DIM is not 1/2/3\n"; - exit(-1); - } - } - // print CIJ to terminal - pcout << "Elasticity matrix (Voigt notation):\n"; - constexpr unsigned int voight_matrix_size = 2 * dim - 1 + dim / 3; - for (unsigned int i = 0; i < voight_matrix_size; i++) - { - for (unsigned int j = 0; j < voight_matrix_size; j++) - { - pcout << std::setw(8) << std::setprecision(3) << std::scientific << CIJ[i][j] - << " "; - } - pcout << "\n"; - } - pcout << "\n"; - return CIJ; -} - -template class userInputParameters<2>; -template class userInputParameters<3>; diff --git a/src/core/inputFileReader.cc b/src/core/user_inputs/input_file_reader.cc similarity index 60% rename from src/core/inputFileReader.cc rename to src/core/user_inputs/input_file_reader.cc index 16129d6f2..496207911 100644 --- a/src/core/inputFileReader.cc +++ b/src/core/user_inputs/input_file_reader.cc @@ -1,9 +1,12 @@ #include #include +#include +#include +#include #include -#include -#include +#include +#include #include inputFileReader::inputFileReader(const std::string &input_file_name, @@ -13,15 +16,12 @@ inputFileReader::inputFileReader(const std::string &input_file_name, , var_attributes(_var_attributes) , pp_attributes(_pp_attributes) { - model_constant_names = get_model_constant_names(); - uint num_constants = model_constant_names.size(); + model_constant_names = get_model_constant_names(); + const uint num_constants = model_constant_names.size(); - if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - std::cout << "Number of constants: " << num_constants << "\n"; - std::cout << "Number of post-processing variables: " << pp_attributes.size() - << "\n"; - } + conditionalOStreams::pout_base + << "Number of constants: " << num_constants << "\n" + << "Number of post-processing variables: " << pp_attributes.size() << "\n"; // Read in all of the parameters now declare_parameters(); @@ -53,7 +53,7 @@ inputFileReader::check_keyword_match(std::string &line, const std::string &keywo } // Check that the line begins with the keyword - for (unsigned int i = 0; i < keyword.size(); i++) + for (uint i = 0; i < keyword.size(); i++) { if (line[i] != keyword[i]) { @@ -140,14 +140,14 @@ inputFileReader::get_subsection_entry_list(const std::string &subsec_name, std::ifstream input_file; input_file.open(parameters_file_name); - std::string line; - std::string entry; - bool in_subsection = false; - bool found_entry = false; - bool desired_entry_found = false; - unsigned int subsection_index = 0; - std::vector entry_list; - std::vector index_list; + std::string line; + std::string entry; + bool in_subsection = false; + bool found_entry = false; + bool desired_entry_found = false; + uint subsection_index = 0; + std::vector entry_list; + std::vector index_list; // Loop through each line while (std::getline(input_file, line)) @@ -191,9 +191,9 @@ inputFileReader::get_subsection_entry_list(const std::string &subsec_name, // Now sort the entry list vector so that it is in index order std::vector sorted_entry_list; - for (unsigned int i = 0; i < entry_list.size(); i++) + for (uint i = 0; i < entry_list.size(); i++) { - for (unsigned int j = 0; j < entry_list.size(); j++) + for (uint j = 0; j < entry_list.size(); j++) { if (i == j) { @@ -286,42 +286,42 @@ void inputFileReader::declare_mesh() { parameter_handler.declare_entry("Number of dimensions", - "-1", - dealii::Patterns::Integer(), + "1", + dealii::Patterns::Integer(1, 3), "The number of dimensions for the simulation."); parameter_handler.declare_entry("Domain size X", - "-1", - dealii::Patterns::Double(), + "0", + dealii::Patterns::Double(0.0, DBL_MAX), "The size of the domain in the x direction."); parameter_handler.declare_entry("Domain size Y", - "-1", - dealii::Patterns::Double(), + "0", + dealii::Patterns::Double(0.0, DBL_MAX), "The size of the domain in the y direction."); parameter_handler.declare_entry("Domain size Z", - "-1", - dealii::Patterns::Double(), + "0", + dealii::Patterns::Double(0.0, DBL_MAX), "The size of the domain in the z direction."); parameter_handler.declare_entry("Element degree", "1", - dealii::Patterns::Integer(), + dealii::Patterns::Integer(1, 6), "The polynomial order of the finte element."); parameter_handler.declare_entry("Subdivisions X", "1", - dealii::Patterns::Integer(), + dealii::Patterns::Integer(1, INT_MAX), "The number of mesh subdivisions in the x direction."); parameter_handler.declare_entry("Subdivisions Y", "1", - dealii::Patterns::Integer(), + dealii::Patterns::Integer(1, INT_MAX), "The number of mesh subdivisions in the y direction."); parameter_handler.declare_entry("Subdivisions Z", "1", - dealii::Patterns::Integer(), + dealii::Patterns::Integer(1, INT_MAX), "The number of mesh subdivisions in the z direction."); parameter_handler.declare_entry( "Refine factor", - "-1", - dealii::Patterns::Integer(), + "0", + dealii::Patterns::Integer(0, INT_MAX), "The number of initial refinements of the coarse mesh."); parameter_handler.declare_entry("Mesh adaptivity", @@ -329,17 +329,17 @@ inputFileReader::declare_mesh() dealii::Patterns::Bool(), "Whether to enable mesh adaptivity."); parameter_handler.declare_entry("Max refinement level", - "-1", - dealii::Patterns::Integer(), + "0", + dealii::Patterns::Integer(0, INT_MAX), "The maximum level of refinement."); parameter_handler.declare_entry("Min refinement level", - "-1", - dealii::Patterns::Integer(), + "0", + dealii::Patterns::Integer(0, INT_MAX), "The minimum level of refinement."); parameter_handler.declare_entry( "Steps between remeshing operations", - "1", - dealii::Patterns::Integer(), + "2147483647", + dealii::Patterns::Integer(1, INT_MAX), "The number of time steps between mesh refinement operations."); for (const auto &[index, variable] : var_attributes) @@ -371,7 +371,7 @@ inputFileReader::declare_mesh() parameter_handler.declare_entry("Gradient magnitude lower bound", "1.0", - dealii::Patterns::Double(), + dealii::Patterns::Double(0.0, DBL_MAX), "The magnitude of the gradient above " "which the mesh should be refined."); } @@ -383,52 +383,73 @@ void inputFileReader::declare_time_discretization() { parameter_handler.declare_entry("Number of time steps", - "-1", - dealii::Patterns::Integer(), + "0", + dealii::Patterns::Integer(0, INT_MAX), "The time step size for the simulation."); parameter_handler.declare_entry("Time step", - "-0.1", - dealii::Patterns::Double(), + "0.0", + dealii::Patterns::Double(0.0, DBL_MAX), "The time step size for the simulation."); parameter_handler.declare_entry( "Simulation end time", - "-0.1", - dealii::Patterns::Double(), + "0.0", + dealii::Patterns::Double(0.0, DBL_MAX), "The value of simulated time where the simulation ends."); } void inputFileReader::declare_solver_parameters() { + // For linear solves for (const auto &[index, variable] : var_attributes) { - if (variable.eq_type == TIME_INDEPENDENT || - variable.eq_type == IMPLICIT_TIME_DEPENDENT) + if (variable.pde_type == TIME_INDEPENDENT || + variable.pde_type == IMPLICIT_TIME_DEPENDENT) { std::string subsection_text = "Linear solver parameters: "; subsection_text.append(variable.name); parameter_handler.enter_subsection(subsection_text); { - parameter_handler.declare_entry("Tolerance type", - "RELATIVE_RESIDUAL_CHANGE", - dealii::Patterns::Anything(), - "The tolerance type for the linear solver."); + parameter_handler.declare_entry( + "Tolerance type", + "ABSOLUTE_RESIDUAL", + dealii::Patterns::Selection("ABSOLUTE_RESIDUAL|RELATIVE_RESIDUAL_CHANGE"), + "The tolerance type for the linear solver."); parameter_handler.declare_entry( "Tolerance value", "1.0e-10", - dealii::Patterns::Double(), + dealii::Patterns::Double(0, DBL_MAX), "The value of for the linear solver tolerance."); parameter_handler.declare_entry( "Maximum linear solver iterations", "1000", - dealii::Patterns::Integer(), + dealii::Patterns::Integer(0, INT_MAX), "The maximum number of linear solver iterations before the loop " "is stopped."); + parameter_handler.declare_entry( + "Preconditioner", + "GMG", + dealii::Patterns::Selection("NONE|GMG"), + "The preconditioner type for the linear solver."); + parameter_handler.declare_entry("Smoothing range", + "15.0", + dealii::Patterns::Double(0, DBL_MAX), + "The smoothing range for eigenvalues."); + parameter_handler.declare_entry("Smoother iterations", + "5", + dealii::Patterns::Integer(0, INT_MAX), + "The number of smoother iterations."); + parameter_handler.declare_entry( + "Eigenvalue CG iterations", + "10", + dealii::Patterns::Integer(0, INT_MAX), + "The maximum number of CG iterations used to find the maximum eigenvalue."); } parameter_handler.leave_subsection(); } } + // For nonlinear solves parameter_handler.declare_entry("Maximum nonlinear solver iterations", "100", dealii::Patterns::Integer(), @@ -437,7 +458,8 @@ inputFileReader::declare_solver_parameters() for (const auto &[index, variable] : var_attributes) { - if (variable.is_nonlinear) + if (variable.field_solve_type == fieldSolveType::NONEXPLICIT_SELF_NONLINEAR || + variable.field_solve_type == fieldSolveType::NONEXPLICIT_CO_NONLINEAR) { std::string subsection_text = "Nonlinear solver parameters: "; subsection_text.append(variable.name); @@ -520,13 +542,13 @@ inputFileReader::declare_output_parameters() "The list of time steps to output, used for the LIST type."); parameter_handler.declare_entry("Number of outputs", "10", - dealii::Patterns::Integer(), + dealii::Patterns::Integer(0, INT_MAX), "The number of outputs (or number of outputs " "per decade for the N_PER_DECADE type)."); parameter_handler.declare_entry( "Skip print steps", - "1", - dealii::Patterns::Integer(), + "2147483647", + dealii::Patterns::Integer(1, INT_MAX), "The number of time steps between updates to the screen."); parameter_handler.declare_entry( "Print timing information with output", @@ -539,51 +561,51 @@ inputFileReader::declare_output_parameters() void inputFileReader::declare_load_IC_parameters() { - parameter_handler.declare_entry( - "Load initial conditions", - "void", - dealii::Patterns::Anything(), - "Whether to load the initial conditions for each variable from file."); - parameter_handler.declare_entry( - "Load parallel file", - "void", - dealii::Patterns::Anything(), - "Whether all processors should read from a single file (versus each " - "reading from separate files)."); - parameter_handler.declare_entry("File names", - "void", - dealii::Patterns::Anything(), - "The file name to load from for each variable."); - parameter_handler.declare_entry( - "Variable names in the files", - "void", - dealii::Patterns::Anything(), - "What each variable is named in the file being loaded."); + // parameter_handler.declare_entry( + // "Load initial conditions", + // "void", + // dealii::Patterns::Anything(), + // "Whether to load the initial conditions for each variable from file."); + // parameter_handler.declare_entry( + // "Load parallel file", + // "void", + // dealii::Patterns::Anything(), + // "Whether all processors should read from a single file (versus each " + // "reading from separate files)."); + // parameter_handler.declare_entry("File names", + // "void", + // dealii::Patterns::Anything(), + // "The file name to load from for each variable."); + // parameter_handler.declare_entry( + // "Variable names in the files", + // "void", + // dealii::Patterns::Anything(), + // "What each variable is named in the file being loaded."); } void inputFileReader::declare_checkpoint_parameters() { - parameter_handler.declare_entry( - "Load from a checkpoint", - "false", - dealii::Patterns::Bool(), - "Whether to load from a checkpoint created during a previous simulation."); - parameter_handler.declare_entry("Checkpoint condition", - "EQUAL_SPACING", - dealii::Patterns::Anything(), - "The spacing type for saving checkpoints."); - parameter_handler.declare_entry( - "List of time steps to save checkpoints", - "0", - dealii::Patterns::Anything(), - "The list of time steps to save checkpoints, used for the LIST type."); - parameter_handler.declare_entry( - "Number of checkpoints", - "0", - dealii::Patterns::Integer(), - "The number of checkpoints (or number of checkpoints per decade for the " - "N_PER_DECADE type)."); + // parameter_handler.declare_entry( + // "Load from a checkpoint", + // "false", + // dealii::Patterns::Bool(), + // "Whether to load from a checkpoint created during a previous simulation."); + // parameter_handler.declare_entry("Checkpoint condition", + // "EQUAL_SPACING", + // dealii::Patterns::Anything(), + // "The spacing type for saving checkpoints."); + // parameter_handler.declare_entry( + // "List of time steps to save checkpoints", + // "0", + // dealii::Patterns::Anything(), + // "The list of time steps to save checkpoints, used for the LIST type."); + // parameter_handler.declare_entry( + // "Number of checkpoints", + // "0", + // dealii::Patterns::Integer(), + // "The number of checkpoints (or number of checkpoints per decade for the " + // "N_PER_DECADE type)."); } void @@ -591,7 +613,7 @@ inputFileReader::declare_BC_parameters() { for (const auto &[index, variable] : var_attributes) { - if (variable.var_type == SCALAR) + if (variable.field_type == SCALAR) { std::string bc_text = "Boundary condition for variable "; bc_text.append(variable.name); @@ -642,17 +664,21 @@ inputFileReader::declare_pinning_parameters() pinning_text.append(variable.name); parameter_handler.enter_subsection(pinning_text); { + parameter_handler.declare_entry("value", + "2147483647", + dealii::Patterns::Double(-DBL_MAX, DBL_MAX), + "Value of pinned point"); parameter_handler.declare_entry("x", - "-1.0", - dealii::Patterns::Double(), + "0.0", + dealii::Patterns::Double(-DBL_MAX, DBL_MAX), "X-coordinate of the point"); parameter_handler.declare_entry("y", "0.0", - dealii::Patterns::Double(), + dealii::Patterns::Double(-DBL_MAX, DBL_MAX), "Y-coordinate of the point"); parameter_handler.declare_entry("z", "0.0", - dealii::Patterns::Double(), + dealii::Patterns::Double(-DBL_MAX, DBL_MAX), "Z-coordinate of the point"); } parameter_handler.leave_subsection(); @@ -662,169 +688,172 @@ inputFileReader::declare_pinning_parameters() void inputFileReader::declare_nucleation_parameters() { - parameter_handler.declare_entry( - "Enable evolution before nucleation", - "false", - dealii::Patterns::Bool(), - "Whether variable fields evolve before the first nucleus appears."); - // Implement later - // parameter_handler.declare_entry("Allow multiple nuclei per order - // parameter","true",dealii::Patterns::Bool(),"Whether multiple nucleation - // events can occur within an order parameter."); - parameter_handler.declare_entry("Minimum allowed distance between nuclei", - "-1", - dealii::Patterns::Double(), - "The minimum allowed distance between nuclei " - "placed during the same time step."); - parameter_handler.declare_entry( - "Order parameter cutoff value", - "0.01", - dealii::Patterns::Double(), - "Order parameter cutoff value for nucleation (when the sum of all order " - "parameters is above this value, no nucleation is attempted)."); - parameter_handler.declare_entry( - "Time steps between nucleation attempts", - "100", - dealii::Patterns::Integer(), - "The number of time steps between nucleation attempts."); - parameter_handler.declare_entry("Nucleation start time", - "0.0", - dealii::Patterns::Double(), - "The time at which nucleation starts."); - parameter_handler.declare_entry("Nucleation end time", - "1.0e10", - dealii::Patterns::Double(), - "The time after which no nucleation occurs."); - - for (const auto &[index, variable] : var_attributes) - { - if (variable.nucleating_variable) - { - std::string nucleation_text = "Nucleation parameters: "; - nucleation_text.append(variable.name); - parameter_handler.enter_subsection(nucleation_text); - { - parameter_handler.declare_entry( - "Nucleus semiaxes (x, y, z)", - "0,0,0", - dealii::Patterns::List(dealii::Patterns::Double()), - "The semiaxes for nuclei placed with the explicit nucleation " - "algorithm."); - parameter_handler.declare_entry( - "Nucleus rotation in degrees (x, y, z)", - "0,0,0", - dealii::Patterns::List(dealii::Patterns::Double()), - "The rotation of the nuclei placed with the explicit nucleation " - "algorithm. The rotations are given with respect to the normal " - "direction using intrinsic Tait-Bryan angles."); - parameter_handler.declare_entry( - "Freeze zone semiaxes (x, y, z)", - "0,0,0", - dealii::Patterns::List(dealii::Patterns::Double()), - "The semiaxes for region where the order parameter is frozen for " - "a period of time after placement."); - parameter_handler.declare_entry( - "Freeze time following nucleation", - "0.0", - dealii::Patterns::Double(), - "Duration that the order parameter is frozen after placement."); - parameter_handler.declare_entry( - "Nucleation-free border thickness", - "0.0", - dealii::Patterns::Double(), - "The thickness of the nucleation-free region near the domain " - "boundaries (ignored for periodic BCs)."); - } - parameter_handler.leave_subsection(); - } - } + // parameter_handler.declare_entry( + // "Enable evolution before nucleation", + // "false", + // dealii::Patterns::Bool(), + // "Whether variable fields evolve before the first nucleus appears."); + // // Implement later + // // parameter_handler.declare_entry("Allow multiple nuclei per order + // // parameter","true",dealii::Patterns::Bool(),"Whether multiple nucleation + // // events can occur within an order parameter."); + // parameter_handler.declare_entry("Minimum allowed distance between nuclei", + // "-1", + // dealii::Patterns::Double(), + // "The minimum allowed distance between nuclei " + // "placed during the same time step."); + // parameter_handler.declare_entry( + // "Order parameter cutoff value", + // "0.01", + // dealii::Patterns::Double(), + // "Order parameter cutoff value for nucleation (when the sum of all order " + // "parameters is above this value, no nucleation is attempted)."); + // parameter_handler.declare_entry( + // "Time steps between nucleation attempts", + // "100", + // dealii::Patterns::Integer(), + // "The number of time steps between nucleation attempts."); + // parameter_handler.declare_entry("Nucleation start time", + // "0.0", + // dealii::Patterns::Double(), + // "The time at which nucleation starts."); + // parameter_handler.declare_entry("Nucleation end time", + // "1.0e10", + // dealii::Patterns::Double(), + // "The time after which no nucleation occurs."); + + // for (const auto &[index, variable] : var_attributes) + // { + // if (variable.nucleating_variable) + // { + // std::string nucleation_text = "Nucleation parameters: "; + // nucleation_text.append(variable.name); + // parameter_handler.enter_subsection(nucleation_text); + // { + // parameter_handler.declare_entry( + // "Nucleus semiaxes (x, y, z)", + // "0,0,0", + // dealii::Patterns::List(dealii::Patterns::Double()), + // "The semiaxes for nuclei placed with the explicit nucleation " + // "algorithm."); + // parameter_handler.declare_entry( + // "Nucleus rotation in degrees (x, y, z)", + // "0,0,0", + // dealii::Patterns::List(dealii::Patterns::Double()), + // "The rotation of the nuclei placed with the explicit nucleation " + // "algorithm. The rotations are given with respect to the normal " + // "direction using intrinsic Tait-Bryan angles."); + // parameter_handler.declare_entry( + // "Freeze zone semiaxes (x, y, z)", + // "0,0,0", + // dealii::Patterns::List(dealii::Patterns::Double()), + // "The semiaxes for region where the order parameter is frozen for " + // "a period of time after placement."); + // parameter_handler.declare_entry( + // "Freeze time following nucleation", + // "0.0", + // dealii::Patterns::Double(), + // "Duration that the order parameter is frozen after placement."); + // parameter_handler.declare_entry( + // "Nucleation-free border thickness", + // "0.0", + // dealii::Patterns::Double(), + // "The thickness of the nucleation-free region near the domain " + // "boundaries (ignored for periodic BCs)."); + // } + // parameter_handler.leave_subsection(); + // } + // } } void inputFileReader::declare_grain_remapping_parameters() { - parameter_handler.declare_entry( - "Activate grain reassignment", - "false", - dealii::Patterns::Bool(), - "Whether to enable the grain reassignment capabilities of PRISMS-PF where " - "multiple grains are packed into a single order parameter."); - - parameter_handler.declare_entry( - "Time steps between grain reassignments", - "100", - dealii::Patterns::Integer(), - "The number of time steps between times when the grain reassignment " - "algorithm is triggered."); - - parameter_handler.declare_entry( - "Order parameter cutoff for grain identification", - "1.0e-4", - dealii::Patterns::Double(), - "The threshold value of the order parameter where the element is " - "considered to be in the grain or out of the grain."); - - parameter_handler.declare_entry( - "Buffer between grains before reassignment", - "-1.0", - dealii::Patterns::Double(), - "The buffer value added to the radius of all grains used to calculation " - "whether grains should be reassigned."); - - parameter_handler.declare_entry( - "Order parameter fields for grain reassignment", - "", - dealii::Patterns::List(dealii::Patterns::Anything()), - "The list of field indices for the shared order parameters for grain " - "reassignment."); + // parameter_handler.declare_entry( + // "Activate grain reassignment", + // "false", + // dealii::Patterns::Bool(), + // "Whether to enable the grain reassignment capabilities of PRISMS-PF where " + // "multiple grains are packed into a single order parameter."); + + // parameter_handler.declare_entry( + // "Time steps between grain reassignments", + // "100", + // dealii::Patterns::Integer(), + // "The number of time steps between times when the grain reassignment " + // "algorithm is triggered."); + + // parameter_handler.declare_entry( + // "Order parameter cutoff for grain identification", + // "1.0e-4", + // dealii::Patterns::Double(), + // "The threshold value of the order parameter where the element is " + // "considered to be in the grain or out of the grain."); + + // parameter_handler.declare_entry( + // "Buffer between grains before reassignment", + // "-1.0", + // dealii::Patterns::Double(), + // "The buffer value added to the radius of all grains used to calculation " + // "whether grains should be reassigned."); + + // parameter_handler.declare_entry( + // "Order parameter fields for grain reassignment", + // "", + // dealii::Patterns::List(dealii::Patterns::Anything()), + // "The list of field indices for the shared order parameters for grain " + // "reassignment."); } void inputFileReader::declare_grain_loading_parameters() { - parameter_handler.declare_entry("Load grain structure", - "false", - dealii::Patterns::Bool(), - "Whether to load a grain structure in from file."); - - parameter_handler.declare_entry( - "vtk file type", - "UNSTRUCTURED", - dealii::Patterns::Anything(), - "Whether to load an unstructured file for grain structure."); // reads the type of - // file from the input - // parameters.prm file, - // deafault setting is - // unstructured mesh - - parameter_handler.declare_entry( - "Grain structure filename", - "", - dealii::Patterns::Anything(), - "The filename (not including the '.vtk' extension) for the file holding " - "the grain structure to be loaded."); - - parameter_handler.declare_entry( - "Grain structure variable name", - "", - dealii::Patterns::Anything(), - "The variable name in the file holding the grain structure to be loaded " - "that contains the grain ids."); - - parameter_handler.declare_entry( - "Number of smoothing cycles after grain structure loading", - "10", - dealii::Patterns::Integer(), - "The number of times a diffusion smoother is run on the order parameters " - "after the grains are loaded from file. The smoothing is necessary for the " - "adaptive mesher to work properly."); - - parameter_handler.declare_entry( - "Minimum radius for loaded grains", - "0.0", - dealii::Patterns::Double(), - "The minimum radius for a body to be considered a grain instead of an " - "artifact from the loading process."); + // parameter_handler.declare_entry("Load grain structure", + // "false", + // dealii::Patterns::Bool(), + // "Whether to load a grain structure in from file."); + + // parameter_handler.declare_entry( + // "vtk file type", + // "UNSTRUCTURED", + // dealii::Patterns::Anything(), + // "Whether to load an unstructured file for grain structure."); // reads the type of + // // file from the + // input + // // parameters.prm + // file, + // // deafault setting + // is + // // unstructured mesh + + // parameter_handler.declare_entry( + // "Grain structure filename", + // "", + // dealii::Patterns::Anything(), + // "The filename (not including the '.vtk' extension) for the file holding " + // "the grain structure to be loaded."); + + // parameter_handler.declare_entry( + // "Grain structure variable name", + // "", + // dealii::Patterns::Anything(), + // "The variable name in the file holding the grain structure to be loaded " + // "that contains the grain ids."); + + // parameter_handler.declare_entry( + // "Number of smoothing cycles after grain structure loading", + // "10", + // dealii::Patterns::Integer(), + // "The number of times a diffusion smoother is run on the order parameters " + // "after the grains are loaded from file. The smoothing is necessary for the " + // "adaptive mesher to work properly."); + + // parameter_handler.declare_entry( + // "Minimum radius for loaded grains", + // "0.0", + // dealii::Patterns::Double(), + // "The minimum radius for a body to be considered a grain instead of an " + // "artifact from the loading process."); } void diff --git a/src/core/user_inputs/user_input_parameters.cc b/src/core/user_inputs/user_input_parameters.cc new file mode 100644 index 000000000..1500525d3 --- /dev/null +++ b/src/core/user_inputs/user_input_parameters.cc @@ -0,0 +1,362 @@ +#include +#include +#include +#include + +template +userInputParameters::userInputParameters(inputFileReader &input_file_reader, + dealii::ParameterHandler ¶meter_handler) + : var_attributes(input_file_reader.var_attributes) + , pp_attributes(input_file_reader.pp_attributes) +{ + // Spatial discretization + assign_spatial_discretization_parameters(parameter_handler); + + // Time stepping parameters + assign_temporal_discretization_parameters(parameter_handler); + + // Output parameters + assign_output_parameters(parameter_handler); + + // Boundary parameters + assign_boundary_parameters(parameter_handler); + + // Linear solve parameters + assign_linear_solve_parameters(parameter_handler); + + // Load the user-defined constants + load_model_constants(input_file_reader, parameter_handler); + + // Print all the parameters to summary.log + spatial_discretization.print_parameter_summary(); + temporal_discretization.print_parameter_summary(); + output_parameters.print_parameter_summary(); + boundary_parameters.print_parameter_summary(); + linear_solve_parameters.print_parameter_summary(); +} + +template +void +userInputParameters::assign_spatial_discretization_parameters( + dealii::ParameterHandler ¶meter_handler) +{ + std::vector axis_labels = {"X", "Y", "Z"}; + for (uint i = 0; i < dim; ++i) + { + spatial_discretization.domain_size[i] = + parameter_handler.get_double("Domain size " + axis_labels[i]); + spatial_discretization.subdivisions[i] = + parameter_handler.get_integer("Subdivisions " + axis_labels[i]); + } + + spatial_discretization.refine_factor = parameter_handler.get_integer("Refine factor"); + + spatial_discretization.degree = parameter_handler.get_integer("Element degree"); + + // Adaptive meshing parameters + spatial_discretization.has_adaptivity = parameter_handler.get_bool("Mesh adaptivity"); + + AssertThrow( + (!spatial_discretization.has_adaptivity || dim != 1), + dealii::ExcMessage( + "Adaptive meshing for the matrix-free method is not currently supported.")); + + spatial_discretization.remeshing_frequency = + parameter_handler.get_integer("Steps between remeshing operations"); + + spatial_discretization.max_refinement_level = + parameter_handler.get_integer("Max refinement level"); + spatial_discretization.min_refinement_level = + parameter_handler.get_integer("Min refinement level"); + + // Enforce that the initial refinement level must be between the max and min + // level + if (spatial_discretization.has_adaptivity && + ((spatial_discretization.refine_factor < + spatial_discretization.min_refinement_level) || + (spatial_discretization.refine_factor > + spatial_discretization.max_refinement_level))) + { + std::cerr << "PRISMS-PF Error: The initial refinement factor must be " + "between the maximum and minimum refinement levels when " + "adaptive meshing is enabled.\n"; + std::cerr << "Initial refinement level: " << spatial_discretization.refine_factor + << " Maximum and minimum refinement levels: " + << spatial_discretization.max_refinement_level << ", " + << spatial_discretization.min_refinement_level << "\n"; + abort(); + } + + // The adaptivity criterion for each variable has its own subsection + for (const auto &[index, variable] : var_attributes) + { + std::string subsection_text = "Refinement criterion: "; + subsection_text.append(variable.name); + + parameter_handler.enter_subsection(subsection_text); + { + const std::string crit_type_string = parameter_handler.get("Criterion type"); + if (!crit_type_string.empty()) + { + RefinementCriterion new_criterion; + new_criterion.variable_index = index; + new_criterion.variable_name = variable.name; + if (boost::iequals(crit_type_string, "VALUE")) + { + new_criterion.criterion_type = criterion_value; + new_criterion.value_lower_bound = + parameter_handler.get_double("Value lower bound"); + new_criterion.value_upper_bound = + parameter_handler.get_double("Value upper bound"); + + // Check to make sure that the upper bound is greater than or + // equal to the lower bound + if (new_criterion.value_upper_bound < new_criterion.value_lower_bound) + { + std::cerr << "PRISMS-PF Error: The upper bound for " + "refinement for variable " + << new_criterion.variable_name + << " is less than the lower bound. Please " + "correct this in the parameters file.\n"; + } + } + else if (boost::iequals(crit_type_string, "GRADIENT")) + { + new_criterion.criterion_type = criterion_gradient; + new_criterion.gradient_lower_bound = + parameter_handler.get_double("Gradient magnitude lower bound"); + } + else if (boost::iequals(crit_type_string, "VALUE_AND_GRADIENT")) + { + new_criterion.criterion_type = criterion_value | criterion_gradient; + new_criterion.value_lower_bound = + parameter_handler.get_double("Value lower bound"); + new_criterion.value_upper_bound = + parameter_handler.get_double("Value upper bound"); + new_criterion.gradient_lower_bound = + parameter_handler.get_double("Gradient magnitude lower bound"); + + // Check to make sure that the upper bound is greater than or + // equal to the lower bound + if (new_criterion.value_upper_bound < new_criterion.value_lower_bound) + { + std::cerr << "PRISMS-PF Error: The upper bound for " + "refinement for variable " + << new_criterion.variable_name + << " is less than the lower bound. Please " + "correct this in the parameters file.\n"; + } + } + else + { + std::cerr << "PRISMS-PF Error: The refinement criteria type " + "found in the parameters file, " + << crit_type_string + << ", is not an allowed type. The allowed types are " + "VALUE, GRADIENT, VALUE_AND_GRADIENT\n"; + abort(); + } + spatial_discretization.refinement_criteria.push_back(new_criterion); + } + } + parameter_handler.leave_subsection(); + } +} + +template +void +userInputParameters::assign_temporal_discretization_parameters( + dealii::ParameterHandler ¶meter_handler) +{ + temporal_discretization.dt = parameter_handler.get_double("Time step"); + temporal_discretization.final_time = + parameter_handler.get_double("Simulation end time"); + temporal_discretization.total_increments = + static_cast(parameter_handler.get_integer("Number of time steps")); + + // If all of the variables are `TIME_INDEPENDENT` or `AUXILIARY`, then total_increments + // should be 1 and final_time should be 0 + bool only_time_independent_pdes = true; + for (const auto &[index, variable] : var_attributes) + { + if (variable.pde_type == PDEType::EXPLICIT_TIME_DEPENDENT || + variable.pde_type == PDEType::IMPLICIT_TIME_DEPENDENT) + { + only_time_independent_pdes = false; + break; + } + } + + if (only_time_independent_pdes) + { + temporal_discretization.total_increments = 1; + return; + } + + AssertThrow(temporal_discretization.dt > 0.0, + dealii::ExcMessage( + "The timestep (dt) must be greater than zero for transient problems.")); + + // Pick the maximum specified time since the default values are zero + temporal_discretization.final_time = + std::max(temporal_discretization.final_time, + temporal_discretization.dt * temporal_discretization.total_increments); + temporal_discretization.total_increments = static_cast( + std::ceil(temporal_discretization.final_time / temporal_discretization.dt)); +} + +template +void +userInputParameters::assign_output_parameters( + dealii::ParameterHandler ¶meter_handler) +{ + output_parameters.output_condition = parameter_handler.get("Output condition"); + output_parameters.n_outputs = + static_cast(parameter_handler.get_integer("Number of outputs")); + output_parameters.user_output_list = + dealii::Utilities::string_to_int(dealii::Utilities::split_string_list( + parameter_handler.get("List of time steps to output"))); + output_parameters.output_frequency = parameter_handler.get_integer("Skip print steps"); + output_parameters.output_file_type = parameter_handler.get("Output file type"); + output_parameters.output_file_name = parameter_handler.get("Output file name (base)"); + output_parameters.print_timing_with_output = + parameter_handler.get_bool("Print timing information with output"); + + // Determine the list of increments where output is neccessary + output_parameters.compute_output_list(temporal_discretization); +} + +template +void +userInputParameters::assign_boundary_parameters( + dealii::ParameterHandler ¶meter_handler) +{ + // Load the boundary condition variables into list of BCs (where each element + // of the vector is one component of one variable) + for (const auto &[index, variable] : var_attributes) + { + if (variable.field_type == SCALAR) + { + std::string bc_text = "Boundary condition for variable "; + bc_text.append(variable.name); + boundary_parameters.BC_list[index].emplace(0, parameter_handler.get(bc_text)); + } + else + { + std::vector axis_labels = {"x", "y", "z"}; + for (uint i = 0; i < dim; i++) + { + std::string bc_text = "Boundary condition for variable "; + bc_text.append(variable.name + ", " + axis_labels[i] + " component"); + boundary_parameters.BC_list[index].emplace(i, + parameter_handler.get(bc_text)); + } + } + } + + // Compute the boundary conditions from the unfiltered string and throw errors, if + // applicable. + boundary_parameters.compute_boundary_conditions(var_attributes); + + for (const auto &[index, variable] : var_attributes) + { + std::string pinning_text = "Pinning point: "; + pinning_text.append(variable.name); + parameter_handler.enter_subsection(pinning_text); + + // Skip if the value is the default INT_MAX + if (parameter_handler.get_double("value") == 2147483647) + { + parameter_handler.leave_subsection(); + continue; + } + + // Otherwise, fill out point and value + std::vector axis_labels = {"x", "y", "z"}; + dealii::Point point; + for (uint i = 0; i < dim; ++i) + { + point[i] = parameter_handler.get_double(axis_labels[i]); + } + boundary_parameters.pinned_point_list + .emplace(index, std::make_pair(parameter_handler.get_double("value"), point)); + + parameter_handler.leave_subsection(); + } +} + +template +void +userInputParameters::assign_linear_solve_parameters( + dealii::ParameterHandler ¶meter_handler) +{ + for (const auto &[index, variable] : var_attributes) + { + if (variable.pde_type == TIME_INDEPENDENT || + variable.pde_type == IMPLICIT_TIME_DEPENDENT) + { + std::string subsection_text = "Linear solver parameters: "; + subsection_text.append(variable.name); + parameter_handler.enter_subsection(subsection_text); + + // Set the tolerance type + const std::string type_string = parameter_handler.get("Tolerance type"); + if (boost::iequals(type_string, "ABSOLUTE_RESIDUAL")) + { + linear_solve_parameters.linear_solve[index].tolerance_type = + solverToleranceType::ABSOLUTE_RESIDUAL; + } + else if (boost::iequals(type_string, "RELATIVE_RESIDUAL_CHANGE")) + { + linear_solve_parameters.linear_solve[index].tolerance_type = + solverToleranceType::RELATIVE_RESIDUAL_CHANGE; + } + + // Set the tolerance value + linear_solve_parameters.linear_solve[index].tolerance = + parameter_handler.get_double("Tolerance value"); + + // Set the maximum number of iterations + linear_solve_parameters.linear_solve[index].max_iterations = + parameter_handler.get_integer("Maximum linear solver iterations"); + + // Set preconditioner type and related parameters + linear_solve_parameters.linear_solve[index].preconditioner = + boost::iequals(parameter_handler.get("Preconditioner"), "GMG") + ? preconditionerType::GMG + : preconditionerType::NONE; + + linear_solve_parameters.linear_solve[index].smoothing_range = + parameter_handler.get_double("Smoothing range"); + + linear_solve_parameters.linear_solve[index].smoother_iterations = + parameter_handler.get_integer("Smoother iterations"); + + linear_solve_parameters.linear_solve[index].eig_cg_n_iterations = + parameter_handler.get_integer("Eigenvalue CG iterations"); + + parameter_handler.leave_subsection(); + } + } +} + +template +void +userInputParameters::load_model_constants( + inputFileReader &input_file_reader, + dealii::ParameterHandler ¶meter_handler) +{ + for (const std::string &constant_name : input_file_reader.model_constant_names) + { + std::string constants_text = "Model constant "; + constants_text.append(constant_name); + + std::vector model_constants_strings = + dealii::Utilities::split_string_list(parameter_handler.get(constants_text)); + + user_constants.model_constants[constant_name] = + user_constants.construct_user_constant(model_constants_strings); + } +} + +INSTANTIATE_UNI_TEMPLATE(userInputParameters) \ No newline at end of file diff --git a/src/core/variableAttributes.cc b/src/core/variableAttributes.cc deleted file mode 100644 index 06caa34f3..000000000 --- a/src/core/variableAttributes.cc +++ /dev/null @@ -1,172 +0,0 @@ -#include - -void -variableAttributes::format_dependencies() -{ - dependencies_RHS.insert(dependencies_value_RHS.begin(), dependencies_value_RHS.end()); - dependencies_RHS.insert(dependencies_gradient_RHS.begin(), - dependencies_gradient_RHS.end()); - - dependencies_LHS.insert(dependencies_value_LHS.begin(), dependencies_value_LHS.end()); - dependencies_LHS.insert(dependencies_gradient_LHS.begin(), - dependencies_gradient_LHS.end()); - - dependencies_PP.insert(dependencies_value_PP.begin(), dependencies_value_PP.end()); - dependencies_PP.insert(dependencies_gradient_PP.begin(), - dependencies_gradient_PP.end()); - - dependency_set.insert(dependencies_RHS.begin(), dependencies_RHS.end()); - dependency_set.insert(dependencies_LHS.begin(), dependencies_LHS.end()); - dependency_set.insert(dependencies_PP.begin(), dependencies_PP.end()); -} - -void -variableAttributes::parse_residual_dependencies() -{ // Check if either is empty and set value and gradient flags for the - // residual appropriately - const std::map< - PDEType, - std::set *, std::set *>>>> - dependencies_for = { - {EXPLICIT_TIME_DEPENDENT, - {{&eval_flags_residual_explicit_RHS, - {&dependencies_value_RHS, &dependencies_gradient_RHS}}}}, - {AUXILIARY, - {{&eval_flags_residual_nonexplicit_RHS, - {&dependencies_value_RHS, &dependencies_gradient_RHS}}}}, - {IMPLICIT_TIME_DEPENDENT, - {{&eval_flags_residual_nonexplicit_RHS, - {&dependencies_value_RHS, &dependencies_gradient_RHS}}, - {&eval_flags_residual_nonexplicit_LHS, - {&dependencies_value_LHS, &dependencies_gradient_LHS}}}}, - {TIME_INDEPENDENT, - {{&eval_flags_residual_nonexplicit_RHS, - {&dependencies_value_RHS, &dependencies_gradient_RHS}}, - {&eval_flags_residual_nonexplicit_LHS, - {&dependencies_value_LHS, &dependencies_gradient_LHS}}}} - }; - - if (is_pp) - { - EvalFlags &residual_flags = eval_flags_residual_postprocess; - const std::set &value_dependency_set = dependencies_value_PP; - const std::set &gradient_dependency_set = dependencies_gradient_PP; - - if (!value_dependency_set.empty()) - { - residual_flags |= dealii::EvaluationFlags::values; - } - if (!gradient_dependency_set.empty()) - { - residual_flags |= dealii::EvaluationFlags::gradients; - } - return; - } - - for (const std::pair *, std::set *>> - &eval_dep_pair : dependencies_for.at(eq_type)) - { - EvalFlags &residual_flags = *(eval_dep_pair.first); - const auto &dep_pair = eval_dep_pair.second; - const std::set &value_dependency_set = *(dep_pair.first); - const std::set &gradient_dependency_set = *(dep_pair.second); - - if (!value_dependency_set.empty()) - { - residual_flags |= dealii::EvaluationFlags::values; - } - if (!gradient_dependency_set.empty()) - { - residual_flags |= dealii::EvaluationFlags::gradients; - } - } -} - -void -variableAttributes::parse_dependencies( - std::map &other_var_attributes) -{ - const std::map relevant_flag { - {"val", dealii::EvaluationFlags::values }, - {"grad", dealii::EvaluationFlags::gradients}, - {"hess", dealii::EvaluationFlags::hessians }, - {"val_change", dealii::EvaluationFlags::values }, - {"grad_change", dealii::EvaluationFlags::gradients}, - {"hess_change", dealii::EvaluationFlags::hessians } - }; - const std::map> delimiters = { - {"val", {"", ""} }, - {"grad", {"grad(", ")"} }, - {"hess", {"hess(", ")"} }, - {"val_change", {"change(", ")"} }, - {"grad_change", {"grad(change(", "))"}}, - {"hess_change", {"hess(change(", "))"}} - }; - - for (const auto &[variation, delimiter] : delimiters) - { - const bool is_change = variation == "val_change" || variation == "grad_change" || - variation == "hess_change"; - const std::string possible_dependency = delimiter.first + name + delimiter.second; - if (is_change && dependency_set.find(possible_dependency) != dependency_set.end()) - { - eval_flags_change_nonexplicit_LHS |= relevant_flag.at(variation); - } - else - { - // `other_variable` refers to the variable with a dependency on `this`. - // `this` is the dependency variable. - for (auto &[other_index, other_variable] : other_var_attributes) - { - if (other_variable.dependency_set.find(possible_dependency) != - other_variable.dependency_set.end()) - { - for (const auto &eval_flag : eval_flags_for_eq_type(other_variable)) - { - *eval_flag |= relevant_flag.at(variation); - } - // Determine whether other_variable is nonlinear (1): LHS has - // dependency on the guess term. - if (other_variable.dependencies_LHS.find(possible_dependency) != - other_variable.dependencies_LHS.end()) - { - other_variable.is_nonlinear |= - (other_variable.eq_type != EXPLICIT_TIME_DEPENDENT) && - (&other_variable == this); - } - // Determine whether other_variable is nonlinear (2): RHS or LHS has - // nonexplicit dependency other than the guess term. - // This is required because PRISMS-PF does concurrent solves. - other_variable.is_nonlinear |= - (other_variable.eq_type != EXPLICIT_TIME_DEPENDENT) && - (eq_type != EXPLICIT_TIME_DEPENDENT) && (&other_variable != this); - } - } - } - } -} - -std::set -variableAttributes::eval_flags_for_eq_type(const variableAttributes &other_variable) -{ - const PDEType other_eq_type = other_variable.eq_type; - if (other_variable.is_pp) - { - return {&eval_flags_postprocess}; - } - if (other_eq_type == EXPLICIT_TIME_DEPENDENT) - { - return {&eval_flags_explicit_RHS}; - } - if (other_eq_type == AUXILIARY) - { - return {&eval_flags_nonexplicit_RHS}; - } - if (other_eq_type == IMPLICIT_TIME_DEPENDENT || other_eq_type == TIME_INDEPENDENT) - { - return {&eval_flags_nonexplicit_RHS, &eval_flags_nonexplicit_LHS}; - } - return {}; -} diff --git a/src/core/variableContainer.cc b/src/core/variableContainer.cc deleted file mode 100644 index 7d5bcf450..000000000 --- a/src/core/variableContainer.cc +++ /dev/null @@ -1,645 +0,0 @@ -#include -#include - -template -variableContainer::variableContainer( - const dealii::MatrixFree &data, - const std::vector &_varInfoList, - const std::vector &_varChangeInfoList) - : varInfoList(_varInfoList) - , varChangeInfoList(_varChangeInfoList) - , num_var(varInfoList.size()) -{ - for (unsigned int i = 0; i < num_var; i++) - { - const auto &var_info = varInfoList[i]; - const auto &var_change_info = varChangeInfoList[i]; - - if (var_info.var_needed) - { - const unsigned int var_index = var_info.global_var_index; - - if (var_info.is_scalar) - { - scalar_vars_map.emplace(var_index, - std::make_unique(data, i)); - } - else - { - vector_vars_map.emplace(var_index, - std::make_unique(data, i)); - } - } - - if (var_change_info.var_needed) - { - const unsigned int var_index = var_change_info.global_var_index; - - if (var_change_info.is_scalar) - { - scalar_change_in_vars_map.emplace(var_index, - std::make_unique(data, i)); - } - else - { - vector_change_in_vars_map.emplace(var_index, - std::make_unique(data, i)); - } - } - } -} - -template -variableContainer::variableContainer( - const dealii::MatrixFree &data, - const std::vector &_varInfoList) - : varInfoList(_varInfoList) - , num_var(varInfoList.size()) -{ - for (unsigned int i = 0; i < num_var; i++) - { - const auto &var_info = varInfoList[i]; - - if (!var_info.var_needed) - { - continue; - } - - const unsigned int var_index = var_info.global_var_index; - - if (var_info.is_scalar) - { - scalar_vars_map.emplace(var_index, std::make_unique(data, i)); - } - else - { - vector_vars_map.emplace(var_index, std::make_unique(data, i)); - } - } -} - -// Variant of the constructor where it reads from a fixed index of "data", used -// for post-processing -template -variableContainer::variableContainer( - const dealii::MatrixFree &data, - const std::vector &_varInfoList, - const unsigned int &fixed_index) - : varInfoList(_varInfoList) - , num_var(varInfoList.size()) -{ - for (unsigned int i = 0; i < num_var; i++) - { - const auto &var_info = varInfoList[i]; - - if (!var_info.var_needed) - { - continue; - } - - const unsigned int var_index = var_info.global_var_index; - - if (var_info.is_scalar) - { - scalar_vars_map.emplace(var_index, - std::make_unique(data, fixed_index)); - } - else - { - vector_vars_map.emplace(var_index, - std::make_unique(data, fixed_index)); - } - } -} - -template -unsigned int -variableContainer::get_num_q_points() const -{ - if (!scalar_vars_map.empty()) - { - return scalar_vars_map.begin()->second->n_q_points; - } - else if (!vector_vars_map.empty()) - { - return vector_vars_map.begin()->second->n_q_points; - } - else if (!scalar_change_in_vars_map.empty()) - { - return scalar_change_in_vars_map.begin()->second->n_q_points; - } - else if (!vector_change_in_vars_map.empty()) - { - return vector_change_in_vars_map.begin()->second->n_q_points; - } - else - { - AssertThrow(false, - dealii::ExcMessage( - "PRISMS-PF Error: When trying to access the number of quadrature " - "points, all FEEvaluation object containers were empty.")); - } -} - -template -dealii::Point -variableContainer::get_q_point_location() const -{ - if (!scalar_vars_map.empty()) - { - return scalar_vars_map.begin()->second->quadrature_point(q_point); - } - else if (!vector_vars_map.empty()) - { - return vector_vars_map.begin()->second->quadrature_point(q_point); - } - else if (!scalar_change_in_vars_map.empty()) - { - return scalar_change_in_vars_map.begin()->second->quadrature_point(q_point); - } - else if (!vector_change_in_vars_map.empty()) - { - return vector_change_in_vars_map.begin()->second->quadrature_point(q_point); - } - else - { - AssertThrow(false, - dealii::ExcMessage( - "PRISMS-PF Error: When trying to access the quadrature point " - "location, all FEEvaluation object containers were empty.")); - } -} - -template -void -variableContainer::reinit_and_eval( - const std::vector *> &src, - unsigned int cell) -{ - for (unsigned int i = 0; i < num_var; i++) - { - const auto &var_info = varInfoList[i]; - - if (!var_info.var_needed) - { - continue; - } - - const unsigned int var_index = var_info.global_var_index; - - if (var_info.is_scalar) - { - auto *scalar_FEEval_ptr = scalar_vars_map[var_index].get(); - scalar_FEEval_ptr->reinit(cell); - scalar_FEEval_ptr->read_dof_values(*src[i]); - scalar_FEEval_ptr->evaluate(var_info.evaluation_flags); - } - else - { - auto *vector_FEEval_ptr = vector_vars_map[var_index].get(); - vector_FEEval_ptr->reinit(cell); - vector_FEEval_ptr->read_dof_values(*src[i]); - vector_FEEval_ptr->evaluate(var_info.evaluation_flags); - } - } -} - -/** - * This is specialized for the LHS where a change in solution is needed. The RHS method - * takes the src as a vector of dealii::LinearAlgebra::distributed::Vectors. - */ -template -void -variableContainer::reinit_and_eval_change_in_solution( - const dealii::LinearAlgebra::distributed::Vector &src, - unsigned int cell, - unsigned int var_being_solved) -{ - if (varChangeInfoList[var_being_solved].is_scalar) - { - auto *scalar_FEEval_ptr = scalar_change_in_vars_map[var_being_solved].get(); - scalar_FEEval_ptr->reinit(cell); - scalar_FEEval_ptr->read_dof_values(src); - scalar_FEEval_ptr->evaluate(varChangeInfoList[var_being_solved].evaluation_flags); - } - else - { - auto *vector_FEEval_ptr = vector_change_in_vars_map[var_being_solved].get(); - vector_FEEval_ptr->reinit(cell); - vector_FEEval_ptr->read_dof_values(src); - vector_FEEval_ptr->evaluate(varChangeInfoList[var_being_solved].evaluation_flags); - } -} - -template -void -variableContainer::reinit(unsigned int cell) -{ - for (unsigned int i = 0; i < num_var; i++) - { - const auto &var_info = varInfoList[i]; - - if (!var_info.var_needed) - { - continue; - } - - const unsigned int var_index = var_info.global_var_index; - - if (var_info.is_scalar) - { - scalar_vars_map[var_index]->reinit(cell); - } - else - { - vector_vars_map[var_index]->reinit(cell); - } - } -} - -template -void -variableContainer::integrate_and_distribute( - std::vector *> &dst) -{ - for (unsigned int i = 0; i < num_var; i++) - { - const auto &var_info = varInfoList[i]; - - if (var_info.residual_flags & dealii::EvaluationFlags::nothing) - { - continue; - } - - const unsigned int var_index = var_info.global_var_index; - - if (var_info.is_scalar) - { - auto *scalar_FEEval_ptr = scalar_vars_map[var_index].get(); - scalar_FEEval_ptr->integrate(var_info.residual_flags); - scalar_FEEval_ptr->distribute_local_to_global(*dst[i]); - } - else - { - auto *vector_FEEval_ptr = vector_vars_map[var_index].get(); - vector_FEEval_ptr->integrate(var_info.residual_flags); - vector_FEEval_ptr->distribute_local_to_global(*dst[i]); - } - } -} - -template -void -variableContainer::integrate_and_distribute_change_in_solution_LHS( - dealii::LinearAlgebra::distributed::Vector &dst, - const unsigned int var_being_solved) -{ - // integrate - if (varChangeInfoList[var_being_solved].is_scalar) - { - auto *scalar_FEEval_ptr = scalar_change_in_vars_map[var_being_solved].get(); - scalar_FEEval_ptr->integrate(varChangeInfoList[var_being_solved].residual_flags); - scalar_FEEval_ptr->distribute_local_to_global(dst); - } - else - { - auto *vector_FEEval_ptr = vector_change_in_vars_map[var_being_solved].get(); - vector_FEEval_ptr->integrate(varChangeInfoList[var_being_solved].residual_flags); - vector_FEEval_ptr->distribute_local_to_global(dst); - } -} - -// Need to add index checking to these functions so that an error is thrown if -// the index wasn't set -template -T -variableContainer::get_scalar_value( - unsigned int global_variable_index) const -{ - if (varInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::values) - { - return scalar_vars_map.at(global_variable_index)->get_value(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a variable value that " - "was not marked as needed in 'equations.cc'. The attempted " - "access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<1, dim, T> -variableContainer::get_scalar_gradient( - unsigned int global_variable_index) const -{ - if (varInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::gradients) - { - return scalar_vars_map.at(global_variable_index)->get_gradient(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a variable gradient " - "that was not marked as needed in 'equations.cc'. The " - "attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<2, dim, T> -variableContainer::get_scalar_hessian( - unsigned int global_variable_index) const -{ - if (varInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::hessians) - { - return scalar_vars_map.at(global_variable_index)->get_hessian(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a variable hessian " - "that was not marked as needed in 'equations.cc'. The " - "attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<1, dim, T> -variableContainer::get_vector_value( - unsigned int global_variable_index) const -{ - if (varInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::values) - { - return vector_vars_map.at(global_variable_index)->get_value(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a variable value that " - "was not marked as needed in 'equations.cc'. The attempted " - "access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<2, dim, T> -variableContainer::get_vector_gradient( - unsigned int global_variable_index) const -{ - if (varInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::gradients) - { - return vector_vars_map.at(global_variable_index)->get_gradient(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a variable gradient " - "that was not marked as needed in 'equations.cc'. The " - "attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<3, dim, T> -variableContainer::get_vector_hessian( - unsigned int global_variable_index) const -{ - if (varInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::hessians) - { - return vector_vars_map.at(global_variable_index)->get_hessian(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a variable hessian " - "that was not marked as needed in 'equations.cc'. The " - "attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -// Need to add index checking to these functions so that an error is thrown if -// the index wasn't set -template -T -variableContainer::get_change_in_scalar_value( - unsigned int global_variable_index) const -{ - if (varChangeInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::values) - { - return scalar_change_in_vars_map.at(global_variable_index)->get_value(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a change in variable " - "value that was not marked as needed in 'equations.cc'. The " - "attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<1, dim, T> -variableContainer::get_change_in_scalar_gradient( - unsigned int global_variable_index) const -{ - if (varChangeInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::gradients) - { - return scalar_change_in_vars_map.at(global_variable_index)->get_gradient(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a change in variable " - "gradient that was not marked as needed in 'equations.cc'. " - "The attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<2, dim, T> -variableContainer::get_change_in_scalar_hessian( - unsigned int global_variable_index) const -{ - if (varChangeInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::hessians) - { - return scalar_change_in_vars_map.at(global_variable_index)->get_hessian(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a change in variable " - "hessian that was not marked as needed in 'equations.cc'. " - "The attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<1, dim, T> -variableContainer::get_change_in_vector_value( - unsigned int global_variable_index) const -{ - if (varChangeInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::values) - { - return vector_change_in_vars_map.at(global_variable_index)->get_value(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a change in variable " - "value that was not marked as needed in 'equations.cc'. The " - "attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<2, dim, T> -variableContainer::get_change_in_vector_gradient( - unsigned int global_variable_index) const -{ - if (varChangeInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::gradients) - { - return vector_change_in_vars_map.at(global_variable_index)->get_gradient(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a change in variable " - "gradient that was not marked as needed in 'equations.cc'. " - "The attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -template -dealii::Tensor<3, dim, T> -variableContainer::get_change_in_vector_hessian( - unsigned int global_variable_index) const -{ - if (varChangeInfoList[global_variable_index].evaluation_flags & - dealii::EvaluationFlags::hessians) - { - return vector_change_in_vars_map.at(global_variable_index)->get_hessian(q_point); - } - else - { - std::cerr << "PRISMS-PF Error: Attempted access of a change in variable " - "hessian that was not marked as needed in 'equations.cc'. " - "The attempted access was for variable with index " - << global_variable_index << " .\n"; - abort(); - } -} - -// The methods to set the residual terms -template -void -variableContainer::set_scalar_value_term_RHS( - unsigned int global_variable_index, - T val) -{ - scalar_vars_map[global_variable_index]->submit_value(val, q_point); -} - -template -void -variableContainer::set_scalar_gradient_term_RHS( - unsigned int global_variable_index, - dealii::Tensor<1, dim, T> grad) -{ - scalar_vars_map[global_variable_index]->submit_gradient(grad, q_point); -} - -template -void -variableContainer::set_vector_value_term_RHS( - unsigned int global_variable_index, - dealii::Tensor<1, dim, T> val) -{ - vector_vars_map[global_variable_index]->submit_value(val, q_point); -} - -template -void -variableContainer::set_vector_gradient_term_RHS( - unsigned int global_variable_index, - dealii::Tensor<2, dim, T> grad) -{ - vector_vars_map[global_variable_index]->submit_gradient(grad, q_point); -} - -template -void -variableContainer::set_scalar_value_term_LHS( - unsigned int global_variable_index, - T val) -{ - scalar_change_in_vars_map[global_variable_index]->submit_value(val, q_point); -} - -template -void -variableContainer::set_scalar_gradient_term_LHS( - unsigned int global_variable_index, - dealii::Tensor<1, dim, T> grad) -{ - scalar_change_in_vars_map[global_variable_index]->submit_gradient(grad, q_point); -} - -template -void -variableContainer::set_vector_value_term_LHS( - unsigned int global_variable_index, - dealii::Tensor<1, dim, T> val) -{ - vector_change_in_vars_map[global_variable_index]->submit_value(val, q_point); -} - -template -void -variableContainer::set_vector_gradient_term_LHS( - unsigned int global_variable_index, - dealii::Tensor<2, dim, T> grad) -{ - vector_change_in_vars_map[global_variable_index]->submit_gradient(grad, q_point); -} - -template class variableContainer<2, 1, dealii::VectorizedArray>; -template class variableContainer<3, 1, dealii::VectorizedArray>; - -template class variableContainer<2, 2, dealii::VectorizedArray>; -template class variableContainer<3, 2, dealii::VectorizedArray>; - -template class variableContainer<2, 3, dealii::VectorizedArray>; -template class variableContainer<3, 3, dealii::VectorizedArray>; - -template class variableContainer<2, 4, dealii::VectorizedArray>; -template class variableContainer<3, 4, dealii::VectorizedArray>; - -template class variableContainer<2, 5, dealii::VectorizedArray>; -template class variableContainer<3, 5, dealii::VectorizedArray>; - -template class variableContainer<2, 6, dealii::VectorizedArray>; -template class variableContainer<3, 6, dealii::VectorizedArray>; \ No newline at end of file diff --git a/src/core/variableAttributeLoader.cc b/src/core/variable_attribute_loader.cc similarity index 53% rename from src/core/variableAttributeLoader.cc rename to src/core/variable_attribute_loader.cc index 14f7d9ccf..bb4ed0d0f 100644 --- a/src/core/variableAttributeLoader.cc +++ b/src/core/variable_attribute_loader.cc @@ -1,7 +1,9 @@ #include +#include + #include -#include +#include void variableAttributeLoader::loadVariableAttributes() @@ -20,36 +22,25 @@ variableAttributeLoader::init_variable_attributes() loadPostProcessorVariableAttributes(); relevant_attributes = nullptr; - for (auto &[pp_index, pp_variable] : pp_attributes) - { - pp_variable.is_pp = true; - Assert(pp_variable.eq_type == EXPLICIT_TIME_DEPENDENT || - pp_variable.eq_type == UNDEFINED_PDE, - dealii::ExcMessage("PRISMS-PF Error: Warning: Postprocess variables can " - "only be explicit.\nProblem postprocessing index: " + - std::to_string(pp_index) + - ", Variable name: " + pp_variable.name + "\n")); - pp_variable.eq_type = EXPLICIT_TIME_DEPENDENT; - } - for (auto &[index, variable] : var_attributes) { variable.format_dependencies(); } - for (auto &[pp_index, pp_variable] : pp_attributes) - { - pp_variable.format_dependencies(); - } validate_attributes(); for (auto &[index, variable] : var_attributes) { variable.parse_residual_dependencies(); variable.parse_dependencies(var_attributes); - variable.parse_dependencies(pp_attributes); } - for (auto &[index, pp_variable] : pp_attributes) + for (auto &[index, variable] : var_attributes) + { + variable.determine_field_solve_type(var_attributes); + } + + // Print variable attributes to summary.log + for (const auto &[index, variable] : var_attributes) { - pp_variable.parse_residual_dependencies(); + variable.print(); } } @@ -67,90 +58,72 @@ variableAttributeLoader::get_pp_attributes() const // Methods to set the various variable attributes void -variableAttributeLoader::set_variable_name(const unsigned int &index, - const std::string &name) const +variableAttributeLoader::set_variable_name(const uint &index, + const std::string &name) const { (*relevant_attributes)[index].name = name; -} - -void -variableAttributeLoader::set_variable_type(const unsigned int &index, - const fieldType &var_type) const -{ - (*relevant_attributes)[index].var_type = var_type; -} -void -variableAttributeLoader::set_variable_equation_type(const unsigned int &index, - const PDEType &var_eq_type) const -{ - (*relevant_attributes)[index].eq_type = var_eq_type; -} - -void -variableAttributeLoader::set_need_value_nucleation(const unsigned int &index, - const bool &flag) const -{ - (*relevant_attributes)[index].need_value_nucleation = flag; + // Also set the field index when setting the name. This should be placed somewhere else + // in the future. + (*relevant_attributes)[index].field_index = index; } void -variableAttributeLoader::set_allowed_to_nucleate(const unsigned int &index, - const bool &flag) const +variableAttributeLoader::set_variable_type(const uint &index, + const fieldType &field_type) const { - (*relevant_attributes)[index].nucleating_variable = flag; + (*relevant_attributes)[index].field_type = field_type; } void -variableAttributeLoader::set_output_integral(const unsigned int &index, - const bool &flag) const +variableAttributeLoader::set_variable_equation_type(const uint &index, + const PDEType &pde_type) const { - (*relevant_attributes)[index].output_integral = flag; - (*relevant_attributes)[index].calc_integral = flag; + (*relevant_attributes)[index].pde_type = pde_type; } void -variableAttributeLoader::set_dependencies_value_term_RHS(const unsigned int &index, - const std::string &dependencies) +variableAttributeLoader::set_dependencies_value_term_RHS(const uint &index, + const std::string &dependencies) { - std::vector dependencies_set = + const std::vector dependencies_set = dealii::Utilities::split_string_list(strip_whitespace(dependencies)); insert_dependencies_value_term_RHS(index, dependencies_set); } void variableAttributeLoader::set_dependencies_gradient_term_RHS( - const unsigned int &index, - const std::string &dependencies) + const uint &index, + const std::string &dependencies) { - std::vector dependencies_set = + const std::vector dependencies_set = dealii::Utilities::split_string_list(strip_whitespace(dependencies)); insert_dependencies_gradient_term_RHS(index, dependencies_set); } void variableAttributeLoader::set_dependencies_value_term_LHS( - const unsigned int &index, - const std::string &dependencies) const + const uint &index, + const std::string &dependencies) const { - std::vector dependencies_set = + const std::vector dependencies_set = dealii::Utilities::split_string_list(strip_whitespace(dependencies)); insert_dependencies_value_term_LHS(index, dependencies_set); } void variableAttributeLoader::set_dependencies_gradient_term_LHS( - const unsigned int &index, - const std::string &dependencies) const + const uint &index, + const std::string &dependencies) const { - std::vector dependencies_set = + const std::vector dependencies_set = dealii::Utilities::split_string_list(strip_whitespace(dependencies)); insert_dependencies_gradient_term_LHS(index, dependencies_set); } template void -variableAttributeLoader::insert_dependencies_value_term_RHS(const unsigned int &index, +variableAttributeLoader::insert_dependencies_value_term_RHS(const uint &index, const Iterable &dependencies) { /* (*relevant_attributes)[index].dependencies_value_RHS.insert(dependencies.begin(), @@ -160,18 +133,13 @@ variableAttributeLoader::insert_dependencies_value_term_RHS(const unsigned int & var_attributes[index].dependencies_value_RHS.insert(dependencies.begin(), dependencies.end()); } - else - { - pp_attributes[index].dependencies_value_PP.insert(dependencies.begin(), - dependencies.end()); - } } template void variableAttributeLoader::insert_dependencies_gradient_term_RHS( - const unsigned int &index, - const Iterable &dependencies) + const uint &index, + const Iterable &dependencies) { /* (*relevant_attributes)[index].dependencies_gradient_RHS.insert(dependencies.begin(), * dependencies.end()); */ @@ -180,18 +148,13 @@ variableAttributeLoader::insert_dependencies_gradient_term_RHS( var_attributes[index].dependencies_gradient_RHS.insert(dependencies.begin(), dependencies.end()); } - else - { - pp_attributes[index].dependencies_gradient_PP.insert(dependencies.begin(), - dependencies.end()); - } } template void variableAttributeLoader::insert_dependencies_value_term_LHS( - const unsigned int &index, - const Iterable &dependencies) const + const uint &index, + const Iterable &dependencies) const { (*relevant_attributes)[index].dependencies_value_LHS.insert(dependencies.begin(), dependencies.end()); @@ -200,8 +163,8 @@ variableAttributeLoader::insert_dependencies_value_term_LHS( template void variableAttributeLoader::insert_dependencies_gradient_term_LHS( - const unsigned int &index, - const Iterable &dependencies) const + const uint &index, + const Iterable &dependencies) const { (*relevant_attributes)[index].dependencies_gradient_LHS.insert(dependencies.begin(), dependencies.end()); @@ -212,7 +175,7 @@ variableAttributeLoader::validate_variable_name( const std::string &name, const std::set &forbidden_names, const std::string &context, - unsigned int index) + uint index) { Assert(!name.empty(), dealii::ExcMessage("PRISMS-PF Error: " + context + @@ -222,8 +185,10 @@ variableAttributeLoader::validate_variable_name( for ([[maybe_unused]] const std::string &forbidden_name : forbidden_names) { std::string error_message = "PRISMS-PF Error: " + context + - " Variable names must not contain \"grad()\", " - "\"hess()\", \"change()\".\nProblem index: "; + " Variable names must not contain \"grad(\", " + "\"hess(\", \"change(\", \"hessdiag(\", \"lap(\", " + "\"div(\", \"symgrad(\", \"curl(\", \"old_1(\", " + "\"old_2(\", \"old_3(\", \"old_4(\".\nProblem index: "; error_message.append(std::to_string(index).append(", Variable name: " + name)); Assert(name.find(forbidden_name) == std::string::npos, @@ -234,16 +199,29 @@ variableAttributeLoader::validate_variable_name( void variableAttributeLoader::populate_dependencies( const std::set> ®_delimiters, + const std::set> &dep_type_delimiters, const std::string &variable_name, - unsigned int index, + uint index, std::set ®_possible_deps, std::map> &change_possible_deps) { - for (const auto &delims : reg_delimiters) + for (const auto ®_delims : reg_delimiters) { - reg_possible_deps.insert(delims.first + variable_name + delims.second); - change_possible_deps[index].insert(delims.first + "change(" + variable_name + ")" + - delims.second); + for (const auto &type_delims : dep_type_delimiters) + { + if (type_delims.first == "change(") + { + change_possible_deps[index].insert(reg_delims.first + type_delims.first + + variable_name + type_delims.second + + reg_delims.second); + } + else + { + reg_possible_deps.insert(reg_delims.first + type_delims.first + + variable_name + type_delims.second + + reg_delims.second); + } + } } } @@ -251,7 +229,7 @@ void variableAttributeLoader::validate_dependencies( const std::set &dependencies, const std::string &context, - unsigned int index, + uint index, const std::string &variable_name, [[maybe_unused]] const std::set ®_possible_deps, [[maybe_unused]] const std::map> &change_possible_deps) @@ -274,52 +252,47 @@ variableAttributeLoader::validate_dependencies( } } -void -variableAttributeLoader::variableAttributeLoader::validate_postprocess_variable( - const std::string &name, - [[maybe_unused]] const std::set &name_list, - [[maybe_unused]] const std::set ®_possible_deps, - const variableAttributes &pp_variable, - unsigned int index) -{ - Assert(name_list.find(name) == name_list.end(), - dealii::ExcMessage("PRISMS-PF Error: Postprocess variable names must be " - "different from solution variable names.\nProblem index: " + - std::to_string(index) + ", Variable name: " + name + "\n")); - for (const auto &PP_dep : pp_variable.dependencies_PP) - { - std::string error_message = - "PRISMS-PF Error: Invalid postprocessing RHS dependency.\nProblem index: "; - error_message.append(std::to_string(index).append(", Variable name: " + name)); - error_message.append(", Invalid dependency name: " + PP_dep + "\n"); - - Assert(reg_possible_deps.find(PP_dep) != reg_possible_deps.end(), - dealii::ExcMessage(error_message)); - } - Assert(pp_variable.dependencies_LHS.empty(), - dealii::ExcMessage("PRISMS-PF Error: Postprocess variables can only have RHS " - "dependencies.\nProblem index: " + - std::to_string(index) + ", Variable name: " + name + "\n")); - Assert(!pp_variable.nucleating_variable && !pp_variable.need_value_nucleation, - dealii::ExcMessage("PRISMS-PF Error: Postprocess variables cannot be used for " - "nucleation.\nProblem index: " + - std::to_string(index) + ", Variable name: " + name + "\n")); -} - void variableAttributeLoader::validate_attributes() { - // Make sure main attributes arent set in pp_attributes - // Make sure dependencies are all variable names and if there are "change()" deps, they - // are correctly placed + // Make sure dependencies are all variable names. If there are change() dependencies + // make sure they are of the same field. Note that this does not enforce them to be on + // the LHS. It also does not enforce the fact div(), symgrad(), or curl() dependencies + // must belong to a vector field. Both of these are done later in variableAttributes. const std::set> reg_delimiters = { - {"", "" }, - {"grad(", ")"}, - {"hess(", ")"} + {"", "" }, + {"grad(", ")"}, + {"hess(", ")"}, + {"hessdiag(", ")"}, + {"lap(", ")"}, + {"div(", ")"}, + {"symgrad(", ")"}, + {"curl(", ")"}, + }; + + const std::set> dep_type_delimiters = { + {"", "" }, + {"change(", ")"}, + {"old_1(", ")"}, + {"old_2(", ")"}, + {"old_3(", ")"}, + {"old_4(", ")"}, }; - const std::set forbidden_names = {"grad(", "hess(", "change("}; + + const std::set forbidden_names = {"grad(", + "hess(", + "change(", + "hessdiag(", + "lap(", + "div(", + "symgrad(", + "curl(", + "old_1(", + "old_2(", + "old_3(", + "old_4("}; + std::set name_list; - std::set pp_name_list; std::set reg_possible_deps; std::map> change_possible_deps; @@ -330,6 +303,7 @@ variableAttributeLoader::validate_attributes() name_list.insert(variable.name); populate_dependencies(reg_delimiters, + dep_type_delimiters, variable.name, index, reg_possible_deps, @@ -338,22 +312,6 @@ variableAttributeLoader::validate_attributes() validate_variable_name(variable.name, forbidden_names, "", index); } - // Validate the postprocessed variables - for (const auto &[pp_index, pp_variable] : pp_attributes) - { - pp_name_list.insert(pp_variable.name); - - validate_variable_name(pp_variable.name, - forbidden_names, - "(postprocess)", - pp_index); - - validate_postprocess_variable(pp_variable.name, - name_list, - reg_possible_deps, - pp_variable, - pp_index); - } // Check dependencies for (const auto &[index, variable] : var_attributes) { @@ -377,6 +335,6 @@ std::string variableAttributeLoader::strip_whitespace(const std::string &_text) { std::string text = _text; - text.erase(std::remove(text.begin(), text.end(), ' '), text.end()); + text.erase(boost::range::remove(text, ' '), text.end()); return text; } \ No newline at end of file diff --git a/src/core/variable_attributes.cc b/src/core/variable_attributes.cc new file mode 100644 index 000000000..41cc5529d --- /dev/null +++ b/src/core/variable_attributes.cc @@ -0,0 +1,380 @@ +#include + +#include +#include +#include + +void +variableAttributes::format_dependencies() +{ + dependencies_RHS.insert(dependencies_value_RHS.begin(), dependencies_value_RHS.end()); + dependencies_RHS.insert(dependencies_gradient_RHS.begin(), + dependencies_gradient_RHS.end()); + + dependencies_LHS.insert(dependencies_value_LHS.begin(), dependencies_value_LHS.end()); + dependencies_LHS.insert(dependencies_gradient_LHS.begin(), + dependencies_gradient_LHS.end()); +} + +void +variableAttributes::parse_residual_dependencies() +{ + // Check if either is empty and set value and gradient flags for the + // residual appropriately. Note that this relies on the fact that only valid + // dependencies are part of the set. + if (!dependencies_value_RHS.empty()) + { + eval_flags_residual_RHS |= dealii::EvaluationFlags::values; + } + if (!dependencies_gradient_RHS.empty()) + { + eval_flags_residual_RHS |= dealii::EvaluationFlags::gradients; + } + if (!dependencies_value_LHS.empty()) + { + eval_flags_residual_LHS |= dealii::EvaluationFlags::values; + } + if (!dependencies_gradient_LHS.empty()) + { + eval_flags_residual_LHS |= dealii::EvaluationFlags::gradients; + } +} + +void +variableAttributes::parse_dependencies(AttributesList &other_var_attributes) +{ + const std::map> relevant_flag = []() + { + // Modifiers for the dependency types + std::vector> dependency_types = { + {"", dependencyType::NORMAL}, + {"_change", dependencyType::CHANGE}, + {"_old_1", dependencyType::OLD_1 }, + {"_old_2", dependencyType::OLD_2 }, + {"_old_3", dependencyType::OLD_3 }, + {"_old_4", dependencyType::OLD_4 }, + }; + + // Dependency evaluation types & their corresponding evaluation flag + std::vector> eval_flags = { + {"value", dealii::EvaluationFlags::values }, + {"gradient", dealii::EvaluationFlags::gradients}, + {"hessian", dealii::EvaluationFlags::hessians }, + {"hessian_diagonal", dealii::EvaluationFlags::hessians }, + {"laplacian", dealii::EvaluationFlags::hessians }, + {"divergence", dealii::EvaluationFlags::gradients}, + {"symmetric_gradient", dealii::EvaluationFlags::gradients}, + {"curl", dealii::EvaluationFlags::gradients}, + }; + + std::map> map; + for (const auto &dep : dependency_types) + { + for (const auto &eval : eval_flags) + { + map[eval.first + dep.first] = {dep.second, eval.second}; + } + } + + return map; + }(); + + const std::map> delimiters = []() + { + // Delimiters for the dependency types. Note that there are + // various overloads for the delimiters. + std::vector>> + dependency_types_delimiters = { + {"", {"", ""} }, + {"_change", {"change(", ")"}}, + {"_old_1", {"old_1(", ")"} }, + {"_old_2", {"old_2(", ")"} }, + {"_old_3", {"old_3(", ")"} }, + {"_old_4", {"old_4(", ")"} }, + }; + + // Dependency evaluation types & their corresponding delimiter. + std::vector>> + base_delimiters = { + {"value", {"", ""} }, + {"gradient", {"grad(", ")"} }, + {"hessian", {"hess(", ")"} }, + {"hessian_diagonal", {"hessdiag(", ")"}}, + {"laplacian", {"lap(", ")"} }, + {"divergence", {"div(", ")"} }, + {"symmetric_gradient", {"symgrad(", ")"} }, + {"curl", {"curl(", ")"} }, + }; + + std::map> map; + for (const auto &dep : dependency_types_delimiters) + { + for (const auto &base : base_delimiters) + { + map[base.first + dep.first] = {base.second.first + dep.second.first, + dep.second.second + base.second.second}; + } + } + + return map; + }(); + + // Helper lambda to validate and fill in dependency sets + auto set_dependencies = + [&](const std::set &dependencies, + std::unordered_map, EvalFlags, pairHash> + &eval_flag_set, + const std::string &context) + { + // Loop through the available delimiters + for (const auto &[variation, delimiter] : delimiters) + { + // Loop through all known variableAttributes + for (auto &[other_index, other_variable] : other_var_attributes) + { + // Grab the potential dependency for the current variable. For example, if we + // are in the variableAttributre for "phi" enumerate all possible dependencies + // for the variable, like "change(phi)". + const std::string possible_dependency = + delimiter.first + other_variable.name + delimiter.second; + + // Populate the dependencies + if (dependencies.find(possible_dependency) != dependencies.end()) + { + dependencyType dep_type = relevant_flag.at(variation).first; + EvalFlags flags = relevant_flag.at(variation).second; + + validate_dependency(variation, dep_type, other_index, context); + + std::pair key = {other_index, dep_type}; + + if (eval_flag_set.find(key) != eval_flag_set.end()) + { + eval_flag_set[key] |= flags; + } + else + { + eval_flag_set.emplace(key, flags); + } + } + } + } + }; + + set_dependencies(dependencies_RHS, eval_flag_set_RHS, "RHS"); + set_dependencies(dependencies_LHS, eval_flag_set_LHS, "LHS"); + + // Compute the dependency_set + compute_dependency_set(other_var_attributes); +} + +void +variableAttributes::determine_field_solve_type(AttributesList &other_var_attributes, + const bool &is_postprocessed) +{ + Assert(!eval_flag_set_RHS.empty(), + dealii::ExcMessage("To begin determining the field solve type, the " + "eval_flag_set_RHS must be populated.")); + + // Early return for explicit solve types + if (pde_type == PDEType::EXPLICIT_TIME_DEPENDENT) + { + is_postprocessed ? field_solve_type = fieldSolveType::EXPLICIT_POSTPROCESS + : field_solve_type = fieldSolveType::EXPLICIT; + return; + } + + // Early return for constant fields + if (pde_type == PDEType::CONSTANT) + { + field_solve_type = fieldSolveType::EXPLICIT_CONSTANT; + return; + } + + // Determine if we co-nonlinearity. In other words, if the solve of this field is + // coupled with the solve of one or more field. This takes precedence over the other + // classifications such as SELF_NONLINEAR. + // + // This co-nonlinearity problem is a cycle detection problem. We employ a simple + // depth-first search algorithm to determine which fields are co-nonlinear. Currently, + // all co-nonlinear fields are lumped together and solved. When dealing with two sets of + // co-nonlinear fields performance may suffer greatly. + find_circular_dependencies(other_var_attributes); + if (field_solve_type == fieldSolveType::NONEXPLICIT_CO_NONLINEAR) + { + return; + } + + // Check for self-nonlinear solves. + bool LHS_has_change_eval_flags = false; + bool LHS_has_normal_eval_flags = false; + + LHS_has_change_eval_flags = + eval_flag_set_LHS.find({field_index, dependencyType::CHANGE}) != + eval_flag_set_LHS.end(); + LHS_has_normal_eval_flags = + eval_flag_set_LHS.find({field_index, dependencyType::NORMAL}) != + eval_flag_set_LHS.end(); + + if (LHS_has_change_eval_flags && LHS_has_normal_eval_flags) + { + field_solve_type = fieldSolveType::NONEXPLICIT_SELF_NONLINEAR; + return; + } + + // Lastly, assign plain linear & auxiliary solves + if (pde_type == PDEType::AUXILIARY) + { + field_solve_type = fieldSolveType::NONEXPLICIT_AUXILIARY; + return; + } + else if (pde_type == PDEType::TIME_INDEPENDENT || + pde_type == PDEType::IMPLICIT_TIME_DEPENDENT) + { + field_solve_type = fieldSolveType::NONEXPLICIT_LINEAR; + return; + } + + // Set undefined is anything falls through our criteria. + field_solve_type = fieldSolveType::UNDEFINED_SOLVE; +} + +void +variableAttributes::print() const +{ + conditionalOStreams::pout_summary() + << "================================================\n" + << "\tVariable attribute for " << name << "\n" + << "================================================\n" + << "Name: " << name << "\n" + << "Index: " << field_index << "\n" + << "Variable type: " << to_string(field_type) << "\n" + << "Equation type: " << to_string(pde_type) << "\n" + << "Field solve type: " << to_string(field_solve_type) << "\n"; + + conditionalOStreams::pout_summary() << "Evaluation flags RHS:\n"; + for (const auto &[key, value] : eval_flag_set_RHS) + { + conditionalOStreams::pout_summary() + << " Index: " << key.first << "\n" + << " Dependency type: " << to_string(key.second) << "\n" + << " Evaluation flags: " << eval_flags_to_string(value) << "\n\n"; + } + + conditionalOStreams::pout_summary() << "Evaluation flags LHS:\n"; + for (const auto &[key, value] : eval_flag_set_LHS) + { + conditionalOStreams::pout_summary() + << " Index: " << key.first << "\n" + << " Dependency type: " << to_string(key.second) << "\n" + << " Evaluation flags: " << eval_flags_to_string(value) << "\n\n"; + } + + conditionalOStreams::pout_summary() + << "Residual flags RHS: " << eval_flags_to_string(eval_flags_residual_RHS) << "\n" + << "Residual flags LHS: " << eval_flags_to_string(eval_flags_residual_LHS) << "\n" + << "\n" + << std::flush; +} + +void +variableAttributes::validate_dependency([[maybe_unused]] const std::string &variation, + [[maybe_unused]] dependencyType dep_type, + [[maybe_unused]] const uint &other_index, + [[maybe_unused]] const std::string &context) const +{ + Assert(context != "RHS" || dep_type != dependencyType::CHANGE, + dealii::ExcMessage("Dependencies with the delimiter change(var) are " + "not allowed on the RHS of any PDE.")); + Assert(context != "LHS" || pde_type == PDEType::IMPLICIT_TIME_DEPENDENT || + pde_type == PDEType::TIME_INDEPENDENT, + dealii::ExcMessage( + "Only `TIME_INDEPENDENT` and `IMPLICIT_TIME_DEPENDENT` fields may " + "have dependencies on the LHS.")); + Assert(dep_type != dependencyType::CHANGE || other_index == field_index, + dealii::ExcMessage( + "Dependencies with the delimiter change(var) are only allowed as " + "dependencies for the same field (e.g, change(phi) is only " + "allowed as a dependency for phi).")); + Assert(field_type == fieldType::VECTOR || + variation.find("divergence") == std::string::npos, + dealii::ExcMessage("Dependencies with the divergence delimiter are " + "only allowed on vector fields.")); + Assert(field_type == fieldType::VECTOR || + variation.find("symmetric_gradient") == std::string::npos, + dealii::ExcMessage("Dependencies with the symmetric gradient delimiter are " + "only allowed on vector fields.")); + Assert(field_type == fieldType::VECTOR || variation.find("curl") == std::string::npos, + dealii::ExcMessage("Dependencies with the curl delimiter are " + "only allowed on vector fields.")); +} + +void +variableAttributes::compute_dependency_set(const AttributesList &other_var_attributes) +{ + // Compute dependencies for a given eval_flag_set. Change and old flags are irrelevant, + // so ignore those. If we have EvalFlags::nothing ignore it. If the dependency is + // explicit or constant ignore it. If the dependency is itself ignore it so it doesn't + // interfere with the map and flag circularity. + auto compute_dependencies = [&](const auto &eval_flag_set) + { + for (const auto &[pair, flag] : eval_flag_set) + { + if (pair.second != dependencyType::NORMAL || flag == EvalFlags::nothing || + other_var_attributes.at(pair.first).pde_type == + PDEType::EXPLICIT_TIME_DEPENDENT || + other_var_attributes.at(pair.first).pde_type == PDEType::CONSTANT || + pair.first == field_index) + { + continue; + } + + dependency_set.insert(pair.first); + } + }; + + compute_dependencies(eval_flag_set_RHS); + compute_dependencies(eval_flag_set_LHS); +} + +void +variableAttributes::find_circular_dependencies(const AttributesList &other_var_attributes) +{ + // Set for visited nodes + std::set visited; + + // Set of nodes in the current recursion stack + std::set current_stack; + + recursive_DFS(other_var_attributes, visited, current_stack, field_index); +} + +void +variableAttributes::recursive_DFS(const AttributesList &other_var_attributes, + std::set &visited, + std::set ¤t_stack, + const uint &vertex) +{ + // Insert the current vertex + visited.insert(vertex); + current_stack.insert(vertex); + + // Loop over the dependencies of the current field (the connected nodes) + for (const auto &dependency : other_var_attributes.at(vertex).dependency_set) + { + // If the current recursion stack already has the dependency, we have a cycle + if (current_stack.find(dependency) != current_stack.end()) + { + field_solve_type = fieldSolveType::NONEXPLICIT_CO_NONLINEAR; + return; + } + // Otherwise, if we haven't already visited this node continue down the graph + if (visited.find(dependency) == visited.end()) + { + recursive_DFS(other_var_attributes, visited, current_stack, dependency); + } + } + + // Remove the node from the current recursion stack + current_stack.erase(vertex); +} diff --git a/src/core/variable_container.cc b/src/core/variable_container.cc new file mode 100644 index 000000000..0dbe9dbf0 --- /dev/null +++ b/src/core/variable_container.cc @@ -0,0 +1,809 @@ +#include + +#include +#include +#include +#include +#include +#include + +template +variableContainer::variableContainer( + const dealii::MatrixFree &data, + const AttributesList &_subset_attributes, + const solveType &_solve_type) + : subset_attributes(_subset_attributes) + , solve_type(_solve_type) +{ + auto construct_map = + [&](const std::unordered_map, EvalFlags, pairHash> + &eval_flag_set) + { + for (const auto &[pair, flags] : eval_flag_set) + { + const uint &dependency_index = pair.first; + if (subset_attributes.at(dependency_index).field_type == fieldType::SCALAR) + { + scalar_vars_map[dependency_index].emplace( + pair.second, + std::make_unique(data, dependency_index)); + } + else + { + vector_vars_map[dependency_index].emplace( + pair.second, + std::make_unique(data, dependency_index)); + } + } + }; + + // Loop through the variable attributes + for (const auto &[index, variable] : subset_attributes) + { + if (solve_type == solveType::NONEXPLICIT_LHS) + { + construct_map(variable.eval_flag_set_LHS); + } + else + { + construct_map(variable.eval_flag_set_RHS); + } + } +} + +template +void +variableContainer::eval_local_operator( + const std::function> &)> + &func, + dealii::LinearAlgebra::distributed::Vector &dst, + const LinearAlgebra::distributed::Vector &src, + const std::pair &cell_range) +{ + for (uint cell = cell_range.first; cell < cell_range.second; ++cell) + { + // Initialize, read DOFs, and set evaulation flags for each variable + reinit_and_eval(src, cell); + + for (uint q = 0; q < get_n_q_points(); ++q) + { + // Set the quadrature point + q_point = q; + + // Grab the quadrature point location + Point> q_point_loc = get_q_point_location(); + + // Calculate the residuals + func(*this, q_point_loc); + } + + // Integrate and add to global vector dst + integrate_and_distribute(dst); + } +} + +template +void +variableContainer::eval_local_diagonal( + const std::function> &)> + &func, + dealii::LinearAlgebra::distributed::Vector &dst, + const std::pair &cell_range, + const uint &global_var_index) +{ + const auto &variable = subset_attributes.at(global_var_index); + + AssertThrow(variable.field_type != fieldType::VECTOR, + FeatureNotImplemented("Vector multigrid")); + + auto *scalar_FEEval_ptr = + scalar_vars_map.at(global_var_index).at(dependencyType::CHANGE).get(); + + n_dofs_per_cell = scalar_FEEval_ptr->dofs_per_cell; + diagonal = std::make_unique>>( + n_dofs_per_cell); + + for (uint cell = cell_range.first; cell < cell_range.second; ++cell) + { + reinit(cell, global_var_index); + + for (uint i = 0; i < n_dofs_per_cell; ++i) + { + for (uint j = 0; j < n_dofs_per_cell; ++j) + { + scalar_FEEval_ptr->submit_dof_value(dealii::VectorizedArray(), j); + } + scalar_FEEval_ptr->submit_dof_value(dealii::make_vectorized_array(1.0), + i); + + eval(global_var_index); + + for (uint q = 0; q < get_n_q_points(); ++q) + { + // Set the quadrature point + q_point = q; + + // Grab the quadrature point location + dealii::Point> q_point_loc = + get_q_point_location(); + + // Calculate the residuals + func(*this, q_point_loc); + } + + integrate(global_var_index); + (*diagonal)[i] = scalar_FEEval_ptr->get_dof_value(i); + } + + for (uint i = 0; i < n_dofs_per_cell; ++i) + { + scalar_FEEval_ptr->submit_dof_value((*diagonal)[i], i); + } + scalar_FEEval_ptr->distribute_local_to_global(dst); + } +} + +template +void +variableContainer::access_valid( + [[maybe_unused]] const uint &global_variable_index, + [[maybe_unused]] const dependencyType &dependency_type, + [[maybe_unused]] const EvalFlags &flag) const +{ + for ([[maybe_unused]] const auto &[index, variable] : subset_attributes) + { + if (solve_type == solveType::NONEXPLICIT_LHS) + { + Assert( + variable.eval_flag_set_LHS.find( + std::pair(global_variable_index, dependency_type)) != + variable.eval_flag_set_LHS.end(), + dealii::ExcMessage("PRISMS-PF Error: Attempted access of a variable that was " + "not marked as needed in 'equations.cc'. The attempted " + "access was for variable with index " + + std::to_string(global_variable_index) + + "with the following dependency type" + + to_string(dependency_type) + ".")); + } + else + { + Assert( + variable.eval_flag_set_RHS.find( + std::pair(global_variable_index, dependency_type)) != + variable.eval_flag_set_RHS.end(), + dealii::ExcMessage("PRISMS-PF Error: Attempted access of a variable that was " + "not marked as needed in 'equations.cc'. The attempted " + "access was for variable with index " + + std::to_string(global_variable_index) + + "with the following dependency type" + + to_string(dependency_type) + ".")); + } + } +} + +template +uint +variableContainer::get_n_q_points() const +{ + if (!scalar_vars_map.empty()) + { + const auto &first_scalar_FEEval = scalar_vars_map.begin()->second; + + Assert(!first_scalar_FEEval.empty(), + dealii::ExcMessage( + "PRISMS-PF Error: When trying to access the number of quadrature " + "points, all FEEvaluation object containers were empty.")); + + return first_scalar_FEEval.begin()->second->n_q_points; + } + else if (!vector_vars_map.empty()) + { + const auto &first_vector_FEEval = vector_vars_map.begin()->second; + + Assert(!first_vector_FEEval.empty(), + dealii::ExcMessage( + "PRISMS-PF Error: When trying to access the number of quadrature " + "points, all FEEvaluation object containers were empty.")); + + return first_vector_FEEval.begin()->second->n_q_points; + } + + Assert(false, + dealii::ExcMessage( + "PRISMS-PF Error: When trying to access the number of quadrature " + "points, all FEEvaluation object containers were empty.")); + return 0; +} + +template +dealii::Point> +variableContainer::get_q_point_location() const +{ + if (!scalar_vars_map.empty()) + { + const auto &first_scalar_FEEval = scalar_vars_map.begin()->second; + + Assert(!first_scalar_FEEval.empty(), + dealii::ExcMessage( + "PRISMS-PF Error: When trying to access the quadrature point " + "location, all FEEvaluation object containers were empty.")); + + return first_scalar_FEEval.begin()->second->quadrature_point(q_point); + } + else if (!vector_vars_map.empty()) + { + const auto &first_vector_FEEval = vector_vars_map.begin()->second; + + Assert(!first_vector_FEEval.empty(), + dealii::ExcMessage( + "PRISMS-PF Error: When trying to access the quadrature point " + "location, all FEEvaluation object containers were empty.")); + + return first_vector_FEEval.begin()->second->quadrature_point(q_point); + } + + Assert(false, + dealii::ExcMessage("PRISMS-PF Error: When trying to access the quadrature point " + "location, all FEEvaluation object containers were empty.")); + + return dealii::Point>(); +} + +template +void +variableContainer::reinit_and_eval( + const dealii::LinearAlgebra::distributed::Vector &src, + uint cell) +{ + auto reinit_and_eval_map = + [&](const std::unordered_map, EvalFlags, pairHash> + &eval_flag_set) + { + for (const auto &[pair, flags] : eval_flag_set) + { + const uint &dependency_index = pair.first; + const dependencyType &dependency_type = pair.second; + + if (subset_attributes.at(dependency_index).field_type == fieldType::SCALAR) + { + auto *scalar_FEEval_ptr = + scalar_vars_map.at(dependency_index).at(dependency_type).get(); + scalar_FEEval_ptr->reinit(cell); + scalar_FEEval_ptr->read_dof_values_plain(src); + scalar_FEEval_ptr->evaluate(flags); + } + else + { + auto *vector_FEEval_ptr = + vector_vars_map.at(dependency_index).at(dependency_type).get(); + vector_FEEval_ptr->reinit(cell); + vector_FEEval_ptr->read_dof_values_plain(src); + vector_FEEval_ptr->evaluate(flags); + } + } + }; + + for (const auto &[var_index, variable] : subset_attributes) + { + if (solve_type == solveType::NONEXPLICIT_LHS) + { + reinit_and_eval_map(variable.eval_flag_set_LHS); + } + else + { + reinit_and_eval_map(variable.eval_flag_set_RHS); + } + } +} + +template +void +variableContainer::reinit(uint cell, + const uint &global_variable_index) +{ + auto reinit_map = + [&](const std::unordered_map, EvalFlags, pairHash> + &eval_flag_set) + { + for (const auto &[pair, flags] : eval_flag_set) + { + const uint &dependency_index = pair.first; + const dependencyType &dependency_type = pair.second; + + if (subset_attributes.at(dependency_index).field_type == fieldType::SCALAR) + { + auto *scalar_FEEval_ptr = + scalar_vars_map.at(dependency_index).at(dependency_type).get(); + scalar_FEEval_ptr->reinit(cell); + } + else + { + auto *vector_FEEval_ptr = + vector_vars_map.at(dependency_index).at(dependency_type).get(); + vector_FEEval_ptr->reinit(cell); + } + } + }; + + if (solve_type == solveType::NONEXPLICIT_LHS) + { + reinit_map(subset_attributes.at(global_variable_index).eval_flag_set_LHS); + } + else + { + reinit_map(subset_attributes.at(global_variable_index).eval_flag_set_RHS); + } +} + +template +void +variableContainer::eval(const uint &global_variable_index) +{ + auto eval_map = + [&](const std::unordered_map, EvalFlags, pairHash> + &eval_flag_set) + { + for (const auto &[pair, flags] : eval_flag_set) + { + const uint &dependency_index = pair.first; + const dependencyType &dependency_type = pair.second; + + if (subset_attributes.at(dependency_index).field_type == fieldType::SCALAR) + { + auto *scalar_FEEval_ptr = + scalar_vars_map.at(dependency_index).at(dependency_type).get(); + scalar_FEEval_ptr->evaluate(flags); + } + else + { + auto *vector_FEEval_ptr = + vector_vars_map.at(dependency_index).at(dependency_type).get(); + vector_FEEval_ptr->evaluate(flags); + } + } + }; + + if (solve_type == solveType::NONEXPLICIT_LHS) + { + eval_map(subset_attributes.at(global_variable_index).eval_flag_set_LHS); + } + else + { + eval_map(subset_attributes.at(global_variable_index).eval_flag_set_RHS); + } +} + +template +void +variableContainer::integrate(const uint &global_variable_index) +{ + const auto &variable = subset_attributes.at(global_variable_index); + + if (solve_type == solveType::NONEXPLICIT_LHS) + { + if (variable.field_type == fieldType::SCALAR) + { + auto *scalar_FEEval_ptr = + scalar_vars_map.at(global_variable_index).at(dependencyType::CHANGE).get(); + scalar_FEEval_ptr->integrate(variable.eval_flags_residual_LHS); + } + else + { + auto *vector_FEEval_ptr = + vector_vars_map.at(global_variable_index).at(dependencyType::CHANGE).get(); + vector_FEEval_ptr->integrate(variable.eval_flags_residual_LHS); + } + } + else + { + if (subset_attributes.at(global_variable_index).field_type == fieldType::SCALAR) + { + auto *scalar_FEEval_ptr = + scalar_vars_map.at(global_variable_index).at(dependencyType::CHANGE).get(); + scalar_FEEval_ptr->integrate(variable.eval_flags_residual_RHS); + } + else + { + auto *vector_FEEval_ptr = + vector_vars_map.at(global_variable_index).at(dependencyType::CHANGE).get(); + vector_FEEval_ptr->integrate(variable.eval_flags_residual_RHS); + } + } +} + +template +void +variableContainer::integrate_and_distribute( + dealii::LinearAlgebra::distributed::Vector &dst) +{ + auto integrate_and_distribute_map = [&](const EvalFlags &residual_flag_set, + const dependencyType &dependency_type, + const uint &residual_index) + { + if (subset_attributes.at(residual_index).field_type == fieldType::SCALAR) + { + auto *scalar_FEEval_ptr = + scalar_vars_map.at(residual_index).at(dependency_type).get(); + scalar_FEEval_ptr->integrate_scatter(residual_flag_set, dst); + } + else + { + auto *vector_FEEval_ptr = + vector_vars_map.at(residual_index).at(dependency_type).get(); + vector_FEEval_ptr->integrate_scatter(residual_flag_set, dst); + } + }; + + for (const auto &[index, variable] : subset_attributes) + { + if (solve_type == solveType::NONEXPLICIT_LHS) + { + integrate_and_distribute_map(variable.eval_flags_residual_LHS, + dependencyType::CHANGE, + index); + } + else + { + integrate_and_distribute_map(variable.eval_flags_residual_RHS, + dependencyType::NORMAL, + index); + } + } +} + +template +dealii::VectorizedArray +variableContainer::get_scalar_value( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::values); +#endif + + return scalar_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_value(q_point); +} + +template +dealii::Tensor<1, dim, dealii::VectorizedArray> +variableContainer::get_scalar_gradient( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::gradients); +#endif + + return scalar_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_gradient(q_point); +} + +template +dealii::Tensor<2, dim, dealii::VectorizedArray> +variableContainer::get_scalar_hessian( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::hessians); +#endif + + return scalar_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_hessian(q_point); +} + +template +dealii::Tensor<1, dim, dealii::VectorizedArray> +variableContainer::get_scalar_hessian_diagonal( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::hessians); +#endif + + return scalar_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_hessian_diagonal(q_point); +} + +template +dealii::VectorizedArray +variableContainer::get_scalar_laplacian( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::hessians); +#endif + + return scalar_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_laplacian(q_point); +} + +template +dealii::Tensor<1, dim, dealii::VectorizedArray> +variableContainer::get_vector_value( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::values); +#endif + + const auto &value = + vector_vars_map.at(global_variable_index).at(dependency_type)->get_value(q_point); + + if constexpr (dim == 1) + { + // Wrap the value for consistency + dealii::Tensor<1, dim, dealii::VectorizedArray> wrapper; + wrapper[0] = value; + return wrapper; + } + else + { + // Return the value directly for dim > 1 + return value; + } +} + +template +dealii::Tensor<2, dim, dealii::VectorizedArray> +variableContainer::get_vector_gradient( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::gradients); +#endif + + const auto &grad = + vector_vars_map.at(global_variable_index).at(dependency_type)->get_gradient(q_point); + + if constexpr (dim == 1) + { + // Wrap the value for consistency + dealii::Tensor<2, dim, dealii::VectorizedArray> wrapper; + wrapper[0] = grad; + return wrapper; + } + else + { + // Return the value directly for dim > 1 + return grad; + } +} + +template +dealii::Tensor<3, dim, dealii::VectorizedArray> +variableContainer::get_vector_hessian( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::hessians); +#endif + + const auto &hess = + vector_vars_map.at(global_variable_index).at(dependency_type)->get_hessian(q_point); + + if constexpr (dim == 1) + { + // Wrap the value for consistency + dealii::Tensor<3, dim, dealii::VectorizedArray> wrapper; + wrapper[0] = hess; + return wrapper; + } + else + { + // Return the value directly for dim > 1 + return hess; + } +} + +template +dealii::Tensor<2, dim, dealii::VectorizedArray> +variableContainer::get_vector_hessian_diagonal( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::hessians); +#endif + + const auto &hess_diag = vector_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_hessian_diagonal(q_point); + + if constexpr (dim == 1) + { + // Wrap the value for consistency + dealii::Tensor<2, dim, dealii::VectorizedArray> wrapper; + wrapper[0] = hess_diag; + return wrapper; + } + else + { + // Return the value directly for dim > 1 + return hess_diag; + } +} + +template +dealii::Tensor<1, dim, dealii::VectorizedArray> +variableContainer::get_vector_laplacian( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::hessians); +#endif + + const auto &lap = + vector_vars_map.at(global_variable_index).at(dependency_type)->get_laplacian(q_point); + + if constexpr (dim == 1) + { + // Wrap the value for consistency + dealii::Tensor<1, dim, dealii::VectorizedArray> wrapper; + wrapper[0] = lap; + return wrapper; + } + else + { + // Return the value directly for dim > 1 + return lap; + } +} + +template +dealii::VectorizedArray +variableContainer::get_vector_divergence( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::gradients); +#endif + + return vector_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_divergence(q_point); +} + +template +dealii::Tensor<2, dim, dealii::VectorizedArray> +variableContainer::get_vector_symmetric_gradient( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::gradients); +#endif + + return vector_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_symmetric_gradient(q_point); +} + +template +dealii::Tensor<1, (dim == 2 ? 1 : dim), dealii::VectorizedArray> +variableContainer::get_vector_curl( + uint global_variable_index, + dependencyType dependency_type) const +{ +#ifdef DEBUG + access_valid(global_variable_index, dependency_type, EvalFlags::gradients); +#endif + + if constexpr (dim == 1) + { + Assert(false, dealii::ExcMessage("PRISMS-PF Error: Curl is nonsensical for 1D.")); + return dealii::Tensor<1, (dim == 2 ? 1 : dim), dealii::VectorizedArray> {}; + } + else + { + // Return the value directly for dim > 1 + return vector_vars_map.at(global_variable_index) + .at(dependency_type) + ->get_curl(q_point); + } +} + +template +void +variableContainer::set_scalar_value_term( + uint global_variable_index, + dealii::VectorizedArray val, + dependencyType dependency_type) +{ +#ifdef DEBUG + Assert( + dependency_type == NORMAL || solve_type == solveType::NONEXPLICIT_LHS, + dealii::ExcMessage( + "PRISMS-PF Error: RHS residuals are only allowed to submit normal value terms.")); + Assert( + dependency_type == CHANGE || solve_type != solveType::NONEXPLICIT_LHS, + dealii::ExcMessage( + "PRISMS-PF Error: LHS residuals are only allowed to submit change value terms.")); +#endif + + scalar_vars_map.at(global_variable_index) + .at(dependency_type) + ->submit_value(val, q_point); +} + +template +void +variableContainer::set_scalar_gradient_term( + uint global_variable_index, + dealii::Tensor<1, dim, dealii::VectorizedArray> grad, + dependencyType dependency_type) +{ +#ifdef DEBUG + Assert(dependency_type == NORMAL || solve_type == solveType::NONEXPLICIT_LHS, + dealii::ExcMessage("PRISMS-PF Error: RHS residuals are only allowed to submit " + "normal gradient terms.")); + Assert(dependency_type == CHANGE || solve_type != solveType::NONEXPLICIT_LHS, + dealii::ExcMessage("PRISMS-PF Error: LHS residuals are only allowed to submit " + "change gradient terms.")); +#endif + + scalar_vars_map.at(global_variable_index) + .at(dependency_type) + ->submit_gradient(grad, q_point); +} + +template +void +variableContainer::set_vector_value_term( + uint global_variable_index, + dealii::Tensor<1, dim, dealii::VectorizedArray> val, + dependencyType dependency_type) +{ +#ifdef DEBUG + Assert( + dependency_type == NORMAL || solve_type == solveType::NONEXPLICIT_LHS, + dealii::ExcMessage( + "PRISMS-PF Error: RHS residuals are only allowed to submit normal value terms.")); + Assert( + dependency_type == CHANGE || solve_type != solveType::NONEXPLICIT_LHS, + dealii::ExcMessage( + "PRISMS-PF Error: LHS residuals are only allowed to submit change value terms.")); +#endif + + vector_vars_map.at(global_variable_index) + .at(dependency_type) + ->submit_value(val, q_point); +} + +template +void +variableContainer::set_vector_gradient_term( + uint global_variable_index, + dealii::Tensor<2, dim, dealii::VectorizedArray> grad, + dependencyType dependency_type) +{ +#ifdef DEBUG + Assert(dependency_type == NORMAL || solve_type == solveType::NONEXPLICIT_LHS, + dealii::ExcMessage("PRISMS-PF Error: RHS residuals are only allowed to submit " + "normal gradient terms.")); + Assert(dependency_type == CHANGE || solve_type != solveType::NONEXPLICIT_LHS, + dealii::ExcMessage("PRISMS-PF Error: LHS residuals are only allowed to submit " + "change gradient terms.")); +#endif + + vector_vars_map.at(global_variable_index) + .at(dependency_type) + ->submit_gradient(grad, q_point); +} + +INSTANTIATE_TRI_TEMPLATE(variableContainer) \ No newline at end of file diff --git a/src/field_input/CMakeLists.txt b/src/field_input/CMakeLists.txt index 90afb46c9..e8104c5d4 100644 --- a/src/field_input/CMakeLists.txt +++ b/src/field_input/CMakeLists.txt @@ -1,5 +1,4 @@ # Manually specify files to be included list(APPEND PRISMS_PF_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/field_input.cc ) set(PRISMS_PF_SOURCE_FILES ${PRISMS_PF_SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/grains/CMakeLists.txt b/src/grains/CMakeLists.txt index 53cf4a41f..e8104c5d4 100644 --- a/src/grains/CMakeLists.txt +++ b/src/grains/CMakeLists.txt @@ -1,8 +1,4 @@ # Manually specify files to be included list(APPEND PRISMS_PF_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/FloodFiller.cc - ${CMAKE_CURRENT_SOURCE_DIR}/OrderParameterRemapper.cc - ${CMAKE_CURRENT_SOURCE_DIR}/reassignGrains.cc - ${CMAKE_CURRENT_SOURCE_DIR}/SimplifiedGrainRepresentation.cc ) set(PRISMS_PF_SOURCE_FILES ${PRISMS_PF_SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/grains/FloodFiller.cc b/src/grains/FloodFiller.cc deleted file mode 100644 index 5ad3ea642..000000000 --- a/src/grains/FloodFiller.cc +++ /dev/null @@ -1,430 +0,0 @@ -#include - -template -void -FloodFiller::calcGrainSets( - [[maybe_unused]] dealii::FESystem &finite_element, - dealii::DoFHandler &dof_handler, - dealii::LinearAlgebra::distributed::Vector *solution_field, - double threshold_lower, - double threshold_upper, - int min_id, - unsigned int order_parameter_index, - std::vector> &grain_sets) -{ - unsigned int grain_index = 0; - - // Loop through the whole mesh and set the user flags to false (so everything - // is considered unmarked) - typename dealii::DoFHandler::cell_iterator cell = dof_handler.begin(); - while (cell != dof_handler.end()) - { - cell->clear_user_flag(); - ++cell; - } - - Grain grain; - grain_sets.push_back(grain); - grain_sets.back().setOrderParameterIndex(order_parameter_index); - - // The flood fill loop - cell = dof_handler.begin(); - while (cell != dof_handler.end()) - { - if (!cell->has_children()) - { - bool grain_assigned = false; - recursiveFloodFill::cell_iterator>( - cell, - dof_handler.end(), - solution_field, - threshold_lower, - threshold_upper, - min_id, - grain_index, - grain_sets, - grain_assigned); - - if (grain_assigned) - { - // Get the grain set initialized for the next grain to be found - grain_index++; - Grain new_grain; - new_grain.setOrderParameterIndex(order_parameter_index); - grain_sets.push_back(new_grain); - } - } - - ++cell; - } - - // If the last grain was initialized but empty, delete it - if (grain_sets.back().getVertexList().size() == 0) - { - grain_sets.pop_back(); - } - - // Generate global list of the grains & send the grain set info to all processors so - // everyone has the full list - if (dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD) > 1) - { - createGlobalGrainSetList(grain_sets); - } - - // Merge grains sharing common vertices - mergeSplitGrains(grain_sets); -} - -template -template -void -FloodFiller::recursiveFloodFill( - T cell, - T cell_end, - dealii::LinearAlgebra::distributed::Vector *solution_field, - double threshold_lower, - double threshold_upper, - int min_id, - unsigned int &grain_index, - std::vector> &grain_sets, - bool &grain_assigned) -{ - if (cell == cell_end) - { - return; - } - - // Check if the cell has been marked yet - if (cell->user_flag_set()) - { - return; - } - - if (cell->has_children()) - { - // Call recursiveFloodFill on the element's children - for (unsigned int n_child = 0; n_child < cell->n_children(); n_child++) - { - recursiveFloodFill(cell->child(n_child), - cell_end, - solution_field, - threshold_lower, - threshold_upper, - min_id, - grain_index, - grain_sets, - grain_assigned); - } - } - else - { - if (!cell->is_locally_owned()) - { - return; - } - - cell->set_user_flag(); - - dealii::FEValues fe_values(*fe, quadrature, dealii::update_values); - - std::vector var_values(num_quad_points); - std::vector> q_point_list(num_quad_points); - - // Get the most common value for the element - fe_values.reinit(cell); - fe_values.get_function_values(*solution_field, var_values); - - std::map quadratureValues; - int maxNumberSeen = 0; - double mostCommonQPointValue = -1; - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - // Add the number of times that var_values[q_point] has - // been seen - if (var_values[q_point] > min_id) - { - ++quadratureValues[var_values[q_point]]; - } - if (quadratureValues[var_values[q_point]] > maxNumberSeen) - { - maxNumberSeen = quadratureValues[var_values[q_point]]; - mostCommonQPointValue = var_values[q_point]; - } - } - double ele_val = mostCommonQPointValue; - - if (ele_val > threshold_lower && ele_val < threshold_upper) - { - grain_assigned = true; - - std::vector> vertex_list; - for (unsigned int vertex_index = 0; - vertex_index < dealii::Utilities::fixed_power(2.0); - vertex_index++) - { - vertex_list.push_back(cell->vertex(vertex_index)); - } - grain_sets.back().addVertexList(vertex_list); - - // Call recursiveFloodFill on the element's neighbors - for (unsigned int n_child = 0; n_child < 2 * dim; n_child++) - { - recursiveFloodFill(cell->neighbor(n_child), - cell_end, - solution_field, - threshold_lower, - threshold_upper, - min_id, - grain_index, - grain_sets, - grain_assigned); - } - } - } -} - -// ================================================================================= -// All-to-all communication of the grain sets -// ================================================================================= -template -void -FloodFiller::createGlobalGrainSetList( - std::vector> &grain_sets) const -{ - unsigned int numProcs = dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD); - - unsigned int num_grains_local = grain_sets.size(); - - // Convert the grain_set object into a group of vectors - std::vector order_parameters; - std::vector num_elements; - std::vector vertices; - - for (unsigned int g = 0; g < grain_sets.size(); g++) - { - order_parameters.push_back(grain_sets.at(g).getOrderParameterIndex()); - - std::vector>> vertex_list = - grain_sets[g].getVertexList(); - num_elements.push_back(vertex_list.size()); - - for (unsigned int c = 0; c < num_elements[g]; c++) - { - for (unsigned int v = 0; v < dealii::Utilities::fixed_power(2.0); v++) - { - for (unsigned int d = 0; d < dim; d++) - { - vertices.push_back(vertex_list[c][v][d]); - } - } - } - } - - unsigned int num_vertices = 0; - for (unsigned int g = 0; g < grain_sets.size(); g++) - { - num_vertices += num_elements[g] * dealii::Utilities::fixed_power(2) * dim; - } - - // Communicate how many grains each core has - std::vector num_grains_per_core(numProcs, 0); - - MPI_Allgather(&num_grains_local, - 1, - MPI_INT, - num_grains_per_core.data(), - 1, - MPI_INT, - MPI_COMM_WORLD); - - int num_grains_global = - std::accumulate(num_grains_per_core.begin(), num_grains_per_core.end(), 0); - - // Communicate the order_parameters - std::vector offset(numProcs, 0); - for (unsigned int n = 1; n < numProcs; n++) - { - offset[n] = offset[n - 1] + num_grains_per_core[n - 1]; - } - - std::vector order_parameters_global(num_grains_global, 0); - - MPI_Allgatherv(order_parameters.data(), - num_grains_local, - MPI_UNSIGNED, - order_parameters_global.data(), - num_grains_per_core.data(), - offset.data(), - MPI_UNSIGNED, - MPI_COMM_WORLD); - - // Communicate the number of elements - std::vector num_elements_global(num_grains_global, 0); - - MPI_Allgatherv(num_elements.data(), - num_grains_local, - MPI_UNSIGNED, - num_elements_global.data(), - num_grains_per_core.data(), - offset.data(), - MPI_UNSIGNED, - MPI_COMM_WORLD); - - // Communicate the vertices - unsigned int total_elements = - std::accumulate(num_elements_global.begin(), num_elements_global.end(), 0); - int num_vertices_global = (unsigned int) total_elements * - dealii::Utilities::fixed_power(2) * (unsigned int) dim; - std::vector vertices_global(num_vertices_global, 0); - - std::vector num_vertices_per_core; - - unsigned int g = 0; - for (unsigned int i = 0; i < numProcs; i++) - { - int num_vert_single_core = 0; - for (int j = 0; j < num_grains_per_core.at(i); j++) - { - num_vert_single_core += num_elements_global.at(g) * - dealii::Utilities::fixed_power(2) * - (unsigned int) dim; - g++; - } - num_vertices_per_core.push_back(num_vert_single_core); - } - - offset.at(0) = 0; - for (unsigned int n = 1; n < numProcs; n++) - { - offset[n] = offset[n - 1] + num_vertices_per_core[n - 1]; - } - - MPI_Allgatherv(vertices.data(), - num_vertices, - MPI_DOUBLE, - vertices_global.data(), - num_vertices_per_core.data(), - offset.data(), - MPI_DOUBLE, - MPI_COMM_WORLD); - - // Put the Grain objects back together - grain_sets.clear(); - - for (int g = 0; g < num_grains_global; g++) - { - Grain new_grain; - for (unsigned int c = 0; c < num_elements_global.at(g); c++) - { - std::vector> verts; - for (unsigned int v = 0; v < dealii::Utilities::fixed_power(2.0); v++) - { - double coords[dim]; - for (unsigned int d = 0; d < dim; d++) - { - coords[d] = vertices_global.front(); - vertices_global.erase(vertices_global.begin()); - } - dealii::Tensor<1, dim> tensor_coords(coords); - dealii::Point vert(tensor_coords); - verts.push_back(vert); - } - new_grain.addVertexList(verts); - } - new_grain.setOrderParameterIndex(order_parameters_global.at(g)); - grain_sets.push_back(new_grain); - } -} - -// ================================================================================= -// Check to see if any grains on different processors share vertices -// ================================================================================= - -template -void -FloodFiller::mergeSplitGrains(std::vector> &grain_sets) const -{ - // Loop though each vertex in the base grain "g" - for (unsigned int g = 0; g < grain_sets.size(); g++) - { - std::vector>> vertex_list = - grain_sets[g].getVertexList(); - - // Now cycle through the other grains to find overlapping elements - for (unsigned int g_other = g + 1; g_other < grain_sets.size(); g_other++) - { - bool matching_vert = false; - - std::vector>> vertex_list_other = - grain_sets[g_other].getVertexList(); - - for (unsigned int c = 0; c < vertex_list.size(); c++) - { - for (unsigned int v = 0; v < dealii::Utilities::fixed_power(2.0); v++) - { - for (unsigned int c_other = 0; c_other < vertex_list_other.size(); - c_other++) - { - for (unsigned int v_other = 0; - v_other < dealii::Utilities::fixed_power(2.0); - v_other++) - { - // Check if the vertices match - if (vertex_list[c][v] == vertex_list_other[c_other][v_other]) - { - matching_vert = true; - break; - } - if (matching_vert) - { - break; - } - } - if (matching_vert) - { - break; - } - } - if (matching_vert) - { - break; - } - } - if (matching_vert) - { - break; - } - } - - if (matching_vert) - { - for (unsigned int c_base = 0; c_base < vertex_list.size(); c_base++) - { - grain_sets[g_other].addVertexList(vertex_list.at(c_base)); - } - grain_sets.erase(grain_sets.begin() + g); - g--; - break; - } - } - } -} - -// Template instantiations -template class FloodFiller<2, 1>; -template class FloodFiller<3, 1>; - -template class FloodFiller<2, 2>; -template class FloodFiller<3, 2>; - -template class FloodFiller<2, 3>; -template class FloodFiller<3, 3>; - -template class FloodFiller<2, 4>; -template class FloodFiller<3, 4>; - -template class FloodFiller<2, 5>; -template class FloodFiller<3, 5>; - -template class FloodFiller<2, 6>; -template class FloodFiller<3, 6>; diff --git a/src/grains/OrderParameterRemapper.cc b/src/grains/OrderParameterRemapper.cc deleted file mode 100644 index 5803290eb..000000000 --- a/src/grains/OrderParameterRemapper.cc +++ /dev/null @@ -1,173 +0,0 @@ -#include - -template -void -OrderParameterRemapper::remap( - std::vector> &grain_representations, - std::vector *> &solution_fields, - dealii::DoFHandler &dof_handler, - unsigned int dofs_per_cell) -{ - for (unsigned int g = 0; g < grain_representations.size(); g++) - { - if (grain_representations.at(g).getOrderParameterId() != - grain_representations.at(g).getOldOrderParameterId()) - { - double transfer_buffer = - std::max(0.0, grain_representations.at(g).getDistanceToNeighbor() / 2.0); - - // For now I have two loops, one where I copy the values from the old - // order parameter to the new one and a second where I zero out the - // old order parameter. This separation prevents writing zero-out - // values to the new order parameter. There probably is a more - // efficient way of doing this. - for (const auto &dof : dof_handler.active_cell_iterators()) - { - if (dof->is_locally_owned()) - { - unsigned int op_new = grain_representations.at(g).getOrderParameterId(); - unsigned int op_old = - grain_representations.at(g).getOldOrderParameterId(); - - // Check if the cell is within the simplified grain - // representation - bool in_grain = true; - for (unsigned int v = 0; - v < dealii::GeometryInfo::vertices_per_cell; - v++) - { - if (dof->vertex(v).distance( - grain_representations.at(g).getCenter()) > - grain_representations.at(g).getRadius() + transfer_buffer) - { - in_grain = false; - break; - } - } - - // If it is, move the values from the old order parameter to - // the new order parameter - if (in_grain) - { - std::vector dof_indices( - dofs_per_cell, - 0); - dof->get_dof_indices(dof_indices); - for (const auto &index : dof_indices) - { - (*solution_fields.at(op_new))[index] = - (*solution_fields.at(op_old))[index]; - } - } - } - } - - for (const auto &dof : dof_handler.active_cell_iterators()) - { - if (dof->is_locally_owned()) - { - unsigned int op_old = - grain_representations.at(g).getOldOrderParameterId(); - - // Check if the cell is within the simplified grain - // representation - bool in_grain = true; - for (unsigned int v = 0; - v < dealii::GeometryInfo::vertices_per_cell; - v++) - { - if (dof->vertex(v).distance( - grain_representations.at(g).getCenter()) > - grain_representations.at(g).getRadius() + transfer_buffer) - { - in_grain = false; - break; - } - } - - // If it is, set the old order parameter to zero - if (in_grain) - { - std::vector dof_indices( - dofs_per_cell, - 0); - dof->get_dof_indices(dof_indices); - - for (const auto &index : dof_indices) - { - (*solution_fields.at(op_old))[index] = 0.0; - } - } - } - } - } - } -} - -template -void -OrderParameterRemapper::remap_from_index_field( - std::vector> &grain_representations, - const dealii::LinearAlgebra::distributed::Vector *grain_index_field, - std::vector *> &solution_fields, - dealii::DoFHandler &dof_handler, - unsigned int dofs_per_cell) -{ - for (unsigned int g = 0; g < grain_representations.size(); g++) - { - std::cout << "Grain: " << grain_representations.at(g).getGrainId() - << " Old OP: " << grain_representations.at(g).getOldOrderParameterId() - << " New OP: " << grain_representations.at(g).getOrderParameterId() - << std::endl; - - double transfer_buffer = - std::max(0.0, grain_representations.at(g).getDistanceToNeighbor() / 2.0); - - // For now I have two loops, one where I copy the values from the old - // order parameter to the new one and a second where I zero out the old - // order parameter. This separation prevents writing zero-out values to - // the new order parameter. There probably is a more efficient way of - // doing this. - for (const auto &dof : dof_handler.active_cell_iterators()) - { - if (dof->is_locally_owned()) - { - unsigned int op_new = grain_representations.at(g).getOrderParameterId(); - - // Check if the cell is within the simplified grain representation - bool in_grain = true; - for (unsigned int v = 0; v < dealii::GeometryInfo::vertices_per_cell; - v++) - { - if (dof->vertex(v).distance(grain_representations.at(g).getCenter()) > - grain_representations.at(g).getRadius() + transfer_buffer) - { - in_grain = false; - break; - } - } - - // If it is, move the values from the old order parameter to the - // new order parameter - if (in_grain) - { - std::vector dof_indices(dofs_per_cell, - 0); - dof->get_dof_indices(dof_indices); - for (const auto &index : dof_indices) - { - if (std::abs((*grain_index_field)[index] - - (double) grain_representations.at(g).getGrainId()) < - 1e-6) - { - (*solution_fields.at(op_new))[index] = 1.0; - } - } - } - } - } - } -} - -template class OrderParameterRemapper<2>; -template class OrderParameterRemapper<3>; diff --git a/src/grains/SimplifiedGrainRepresentation.cc b/src/grains/SimplifiedGrainRepresentation.cc deleted file mode 100644 index f96339d1c..000000000 --- a/src/grains/SimplifiedGrainRepresentation.cc +++ /dev/null @@ -1,301 +0,0 @@ -#include - -// ============================================================================ -// Methods for SimplifiedGrainRepresentation -// ============================================================================ - -template -SimplifiedGrainRepresentation::SimplifiedGrainRepresentation(const Grain &grain) - : grain_id(grain.getGrainIndex()) - , order_parameter_id(grain.getOrderParameterIndex()) - , old_order_parameter_id(order_parameter_id) - , distance_to_neighbor_sharing_op(0.0) -{ - // Calculate the centroid assuming that the elements are rectangular and with - // no weighting based on the actual value of the field - std::vector>> vertex_list = grain.getVertexList(); - - double grain_volume = 0.0; - dealii::Tensor<1, dim> centroid; - - for (auto &vertex : vertex_list) - { - double cell_volume = 1.0; - dealii::Point cell_center; - - unsigned int opposite_corner_index = 0; - if (dim == 2) - { - opposite_corner_index = 3; - } - else - { - opposite_corner_index = 7; - } - - for (unsigned int dimension = 0; dimension < dim; dimension++) - { - cell_volume *= - (vertex[opposite_corner_index][dimension] - vertex[0][dimension]); - cell_center(dimension) = - (vertex[opposite_corner_index][dimension] + vertex[0][dimension]) / 2.0; - } - - for (unsigned int dimension = 0; dimension < dim; dimension++) - { - centroid[dimension] += cell_volume * cell_center(dimension); - } - - grain_volume += cell_volume; - } - - centroid /= grain_volume; - - for (unsigned int dimension = 0; dimension < dim; dimension++) - { - center(dimension) = centroid[dimension]; - } - - // Calculate the radius as the largest distance from the centroid to one of - // the vertices - radius = 0.0; - for (auto &vertex : vertex_list) - { - for (unsigned int vertex_index = 0; - vertex_index < dealii::Utilities::fixed_power(2.0); - vertex_index++) - { - if (vertex[vertex_index].distance(center) > radius) - { - radius = vertex[vertex_index].distance(center); - } - } - } -} - -template -dealii::Point -SimplifiedGrainRepresentation::getCenter() const -{ - return center; -} - -template -double -SimplifiedGrainRepresentation::getRadius() const -{ - return radius; -} - -template -unsigned int -SimplifiedGrainRepresentation::getGrainId() const -{ - return grain_id; -} - -template -void -SimplifiedGrainRepresentation::setGrainId(unsigned int _grain_id) -{ - grain_id = _grain_id; -} - -template -unsigned int -SimplifiedGrainRepresentation::getOrderParameterId() const -{ - return order_parameter_id; -} - -template -void -SimplifiedGrainRepresentation::setOrderParameterId(unsigned int _order_parameter_id) -{ - order_parameter_id = _order_parameter_id; -} - -template -unsigned int -SimplifiedGrainRepresentation::getOldOrderParameterId() const -{ - return old_order_parameter_id; -} - -template -void -SimplifiedGrainRepresentation::setDistanceToNeighbor(double dist) -{ - distance_to_neighbor_sharing_op = dist; -} - -template -double -SimplifiedGrainRepresentation::getDistanceToNeighbor() const -{ - return distance_to_neighbor_sharing_op; -} - -// ============================================================================ -// Methods for SimplifiedGrainManipulator -// ============================================================================ - -template -void -SimplifiedGrainManipulator::reassignGrains( - std::vector> &grain_representations, - double buffer_distance, - std::vector &order_parameter_id_list) -{ - for (int cycle = order_parameter_id_list.size(); cycle >= 0; cycle--) - { - for (unsigned int g_base = 0; g_base < grain_representations.size(); g_base++) - { - unsigned int order_parameter_base = - grain_representations.at(g_base).getOrderParameterId(); - - for (unsigned int g_other = 0; g_other < grain_representations.size(); - g_other++) - { - if (g_other != g_base) - { - unsigned int order_parameter_other = - grain_representations.at(g_other).getOrderParameterId(); - - // Check for overlap between the base grain and the other - // grain - double center_distance = - grain_representations.at(g_base).getCenter().distance( - grain_representations.at(g_other).getCenter()); - double sum_radii = grain_representations.at(g_base).getRadius() + - grain_representations.at(g_other).getRadius(); - - if ((sum_radii + 2.0 * buffer_distance > center_distance) and - (order_parameter_other == order_parameter_base)) - { - if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == 0) - { - std::cout << "Found overlap between grain " - << grain_representations.at(g_base).getGrainId() - << " and grain " - << grain_representations.at(g_other).getGrainId() - << " with order parameter " << order_parameter_base - << std::endl; - } - - grain_representations.at(g_base).setDistanceToNeighbor( - center_distance - sum_radii); - - // Another loop over all of the grains to find the order - // parameter with the largest minimum distance to the base - // grain - std::vector minimum_distance_list( - order_parameter_id_list.size(), - std::numeric_limits::max()); - - for (unsigned int g_spacing_list = 0; - g_spacing_list < grain_representations.size(); - g_spacing_list++) - { - if (g_spacing_list != g_base) - { - unsigned int order_parameter_spacing_list = - grain_representations.at(g_spacing_list) - .getOrderParameterId(); - - double spacing = - grain_representations.at(g_base).getCenter().distance( - grain_representations.at(g_spacing_list).getCenter()) - - grain_representations.at(g_base).getRadius() - - grain_representations.at(g_spacing_list).getRadius(); - - if (spacing < - minimum_distance_list.at(order_parameter_spacing_list)) - { - minimum_distance_list.at(order_parameter_spacing_list) = - spacing; - } - } - } - // Pick the max value of minimum_distance_list to - // determine which order parameter to switch the base - // grain to Reassign the order parameter for the grains - // with the conflicts with the most other order - // parameters. In the very last cycle, the grains that - // only have conflicts in their own order parameter are - // reassigned. - double max_distance = -std::numeric_limits::max(); - unsigned int new_op_index = 0; - int overlap_counter = 0; - for (unsigned int op = 0; op < minimum_distance_list.size(); op++) - { - if (minimum_distance_list.at(op) > max_distance) - { - max_distance = minimum_distance_list.at(op); - new_op_index = op; - } - if (minimum_distance_list.at(op) < 0) - { - overlap_counter++; - } - } - if (overlap_counter >= cycle) - { - grain_representations.at(g_base).setOrderParameterId( - new_op_index); - order_parameter_base = new_op_index; - - if (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD) == - 0) - { - std::cout - << "Reassigning grain " - << grain_representations.at(g_base).getGrainId() - << " from order parameter " - << grain_representations.at(g_base) - .getOldOrderParameterId() - << " to order parameter " - << grain_representations.at(g_base).getOrderParameterId() - << std::endl - << std::endl; - } - } - } - } - } - } - } -} - -template -void -SimplifiedGrainManipulator::transferGrainIds( - const std::vector> &old_grain_representations, - std::vector> &new_grain_representations) const -{ - for (unsigned int g_new = 0; g_new < new_grain_representations.size(); g_new++) - { - double min_distance = std::numeric_limits::max(); - unsigned int index_at_min_distance = 0; - - for (unsigned int g_old = 0; g_old < old_grain_representations.size(); g_old++) - { - double distance = new_grain_representations.at(g_new).getCenter().distance( - old_grain_representations.at(g_old).getCenter()); - - if (distance < min_distance) - { - min_distance = distance; - index_at_min_distance = old_grain_representations.at(g_old).getGrainId(); - } - } - new_grain_representations.at(g_new).setGrainId(index_at_min_distance); - } -} - -// Template instantiations -template class SimplifiedGrainManipulator<2>; -template class SimplifiedGrainManipulator<3>; - -template class SimplifiedGrainRepresentation<2>; -template class SimplifiedGrainRepresentation<3>; diff --git a/src/grains/reassignGrains.cc b/src/grains/reassignGrains.cc deleted file mode 100644 index c5386388c..000000000 --- a/src/grains/reassignGrains.cc +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include -#include -#include - -// vmult operation for LHS -template -void -MatrixFreePDE::reassignGrains() -{ - // log time - computing_timer.enter_subsection("matrixFreePDE: reassignGrains"); - - pcout << "Reassigning grains...\n"; - - // Get the index of the first scalar field (used to get the FE object and - // DOFHandler) - unsigned int scalar_field_index = 0; - for (const auto &[index, variable] : var_attributes) - { - if (variable.var_type == SCALAR) - { - scalar_field_index = index; - break; - } - } - - // Create the simplified grain representations - QGaussLobatto quadrature2(degree + 1); - FloodFiller flood_filler(*FESet.at(scalar_field_index), quadrature2); - - std::vector> grain_sets; - - unsigned int op_list_index = 0; - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - if (op_list_index < userInputs.variables_for_remapping.size()) - { - if (fieldIndex == userInputs.variables_for_remapping.at(op_list_index)) - { - op_list_index++; - - std::vector> single_OP_grain_sets; - flood_filler.calcGrainSets(*FESet.at(scalar_field_index), - *dofHandlersSet_nonconst.at(scalar_field_index), - solutionSet.at(fieldIndex), - userInputs.order_parameter_threshold, - 1.0 + userInputs.order_parameter_threshold, - 0, - fieldIndex, - single_OP_grain_sets); - - grain_sets.insert(grain_sets.end(), - single_OP_grain_sets.begin(), - single_OP_grain_sets.end()); - } - } - } - - // Set the grain indices to unique values - for (unsigned int g = 0; g < grain_sets.size(); g++) - { - grain_sets.at(g).setGrainIndex(g); - } - - std::vector> old_grain_representations = - simplified_grain_representations; - simplified_grain_representations.clear(); - for (unsigned int g = 0; g < grain_sets.size(); g++) - { - SimplifiedGrainRepresentation simplified_grain_representation( - grain_sets.at(g)); - - pcout << "Grain: " << simplified_grain_representation.getGrainId() << " " - << simplified_grain_representation.getOrderParameterId() - << " Center: " << simplified_grain_representation.getCenter()(0) << " " - << simplified_grain_representation.getCenter()(1) << std::endl; - - simplified_grain_representations.push_back(simplified_grain_representation); - } - - SimplifiedGrainManipulator simplified_grain_manipulator; - - if (currentIncrement > 0 || userInputs.load_grain_structure) - { - simplified_grain_manipulator.transferGrainIds(old_grain_representations, - simplified_grain_representations); - } - - simplified_grain_manipulator.reassignGrains(simplified_grain_representations, - userInputs.buffer_between_grains, - userInputs.variables_for_remapping); - - for (unsigned int g = 0; g < this->simplified_grain_representations.size(); g++) - { - pcout << "Grain: " << simplified_grain_representations[g].getGrainId() << " " - << simplified_grain_representations[g].getOrderParameterId() - << " Center: " << simplified_grain_representations[g].getCenter()(0) << " " - << simplified_grain_representations[g].getCenter()(1) << std::endl; - } - - OrderParameterRemapper order_parameter_remapper; - order_parameter_remapper.remap(simplified_grain_representations, - solutionSet, - *dofHandlersSet_nonconst.at(scalar_field_index), - FESet.at(scalar_field_index)->dofs_per_cell); - - pcout << "Reassigning grains completed.\n\n"; - - // end log - computing_timer.leave_subsection("matrixFreePDE: reassignGrains"); -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/nucleation/CMakeLists.txt b/src/nucleation/CMakeLists.txt index bca3c4f2a..e8104c5d4 100644 --- a/src/nucleation/CMakeLists.txt +++ b/src/nucleation/CMakeLists.txt @@ -1,6 +1,4 @@ # Manually specify files to be included list(APPEND PRISMS_PF_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/nucleation.cc - ${CMAKE_CURRENT_SOURCE_DIR}/parallelNucleationList.cc ) set(PRISMS_PF_SOURCE_FILES ${PRISMS_PF_SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/nucleation/nucleation.cc b/src/nucleation/nucleation.cc deleted file mode 100644 index 55deab9f0..000000000 --- a/src/nucleation/nucleation.cc +++ /dev/null @@ -1,606 +0,0 @@ -// Methods in MatrixFreePDE to update the list of nuclei -#include -#include -#include -#include -#include -#include - -// ======================================================================================================= -// Function called in solve to update the global list of nuclei -// ======================================================================================================= -template -void -MatrixFreePDE::updateNucleiList() -{ - if (userInputs.nucleation_occurs) - { - if (currentIncrement % userInputs.steps_between_nucleation_attempts == 0 || - currentIncrement == 1) - { - if (userInputs.dtValue * (double) currentIncrement >= - userInputs.nucleation_start_time && - userInputs.dtValue * (double) currentIncrement <= - userInputs.nucleation_end_time) - { - computing_timer.enter_subsection("matrixFreePDE: nucleation"); - // Apply constraints - for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) - { - constraintsDirichletSet[fieldIndex]->distribute( - *solutionSet[fieldIndex]); - constraintsOtherSet[fieldIndex]->distribute(*solutionSet[fieldIndex]); - solutionSet[fieldIndex]->update_ghost_values(); - } - - std::vector> new_nuclei; - if (currentIncrement == 1 && !userInputs.evolution_before_nucleation) - { - while (new_nuclei.size() == 0) - { - currentTime += - userInputs.dtValue * - (double) userInputs.steps_between_nucleation_attempts; - currentIncrement += userInputs.steps_between_nucleation_attempts; - - while (userInputs.outputTimeStepList.size() > 0 && - userInputs.outputTimeStepList[currentOutput] < - currentIncrement) - { - currentOutput++; - } - - while (userInputs.checkpointTimeStepList.size() > 0 && - userInputs.checkpointTimeStepList[currentCheckpoint] < - currentIncrement) - { - currentCheckpoint++; - } - - new_nuclei = getNewNuclei(); - } - } - else - { - new_nuclei = getNewNuclei(); - } - nuclei.insert(nuclei.end(), new_nuclei.begin(), new_nuclei.end()); - - if (new_nuclei.size() > 0 && userInputs.h_adaptivity == true) - { - refineMeshNearNuclei(new_nuclei); - } - computing_timer.leave_subsection("matrixFreePDE: nucleation"); - } - } - } -} - -// ======================================================================================================= -// Core method to perform a nucleation check -// ======================================================================================================= -template -std::vector> -MatrixFreePDE::getNewNuclei() -{ - // Declare a vector of all the NEW nuclei seeded in this time step - std::vector> newnuclei; - - // Get list of prospective new nuclei for the local processor - pcout << "Nucleation attempt for increment " << currentIncrement << "\n"; - - getLocalNucleiList(newnuclei); - pcout << "nucleation attempt! " << currentTime << " " << currentIncrement << "\n"; - - // Generate global list of new nuclei and resolve conflicts between new nuclei - parallelNucleationList new_nuclei_parallel(newnuclei); - newnuclei = - new_nuclei_parallel.buildGlobalNucleiList(userInputs.min_distance_between_nuclei, - nuclei.size()); - - // Final check to resolve overlap conflicts with existing precipitates - std::vector conflict_ids; - safetyCheckNewNuclei(newnuclei, conflict_ids); - - newnuclei = new_nuclei_parallel.removeSubsetOfNuclei(conflict_ids, nuclei.size()); - - return newnuclei; -} - -// ================================================================================= -// Get list of prospective new nuclei for the local processor -// ================================================================================= -template -void -MatrixFreePDE::getLocalNucleiList(std::vector> &newnuclei) const -{ - // Nickname for current time and time step - double t = currentTime; - unsigned int inc = currentIncrement; - - // QGauss quadrature(degree+1); - QGaussLobatto quadrature(degree + 1); - FEValues fe_values(*(FESet[0]), - quadrature, - update_values | update_quadrature_points | update_JxW_values); - const unsigned int num_quad_points = quadrature.size(); - std::vector> var_values(userInputs.nucleation_need_value.size(), - std::vector(num_quad_points)); - std::vector> q_point_list(num_quad_points); - - std::vector> q_point_list_overlap(num_quad_points); - - // What used to be in nuc_attempt - double rand_val = NAN; - // Better random no. generator - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution<> distr(0.0, 1.0); - - // Element cycle - for (const auto &dof : dofHandlersSet_nonconst[0]->active_cell_iterators()) - { - if (dof->is_locally_owned()) - { - // Obtaining average element concentration by averaging over element's - // quadrature points - fe_values.reinit(dof); - for (unsigned int var = 0; var < userInputs.nucleation_need_value.size(); var++) - { - fe_values.get_function_values( - *(solutionSet[userInputs.nucleation_need_value[var]]), - var_values[var]); - } - q_point_list = fe_values.get_quadrature_points(); - - // --------------------------- - // NOTE: This might not be the best way to do this. This is missing - // the loop of the DoFs from Step-3 - - double element_volume = 0.0; - dealii::Point ele_center; - // Loop over the quadrature points to find the element volume (or area - // in 2D) and the average q point location - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - element_volume = element_volume + fe_values.JxW(q_point); - for (unsigned int i = 0; i < dim; i++) - { - ele_center[i] = - ele_center[i] + q_point_list[q_point](i) / ((double) num_quad_points); - } - } - - // Loop over each variable and each quadrature point to get the - // average variable value for the element - variableValueContainer variable_values; - for (unsigned int var = 0; var < userInputs.nucleation_need_value.size(); var++) - { - double ele_val = 0.0; - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - ele_val = ele_val + var_values[var][q_point] * fe_values.JxW(q_point); - } - ele_val /= element_volume; - variable_values.set(userInputs.nucleation_need_value[var], ele_val); - } - - // Loop through each nucleating order parameter - for (unsigned int i = 0; i < userInputs.nucleating_variable_indices.size(); i++) - { - unsigned int variable_index = userInputs.nucleating_variable_indices.at(i); - - // Compute random no. between 0 and 1 (new method) - rand_val = distr(gen); - // Nucleation probability - double Prob = getNucleationProbability(variable_values, - element_volume, - ele_center, - variable_index); - - // ---------------------------- - - if (rand_val <= Prob) - { - // Initializing random vector in "dim" dimensions - std::vector randvec(dim, 0.0); - dealii::Point nuc_ele_pos; - - // Finding coordinates of quadrature point closest to and - // furthest away from the origin - std::vector ele_origin(dim); - for (unsigned int i = 0; i < dim; i++) - { - ele_origin[i] = q_point_list[0](i); - } - std::vector ele_max(dim); - for (unsigned int i = 0; i < dim; i++) - { - ele_max[i] = q_point_list[0](i); - } - for (unsigned int i = 0; i < dim; i++) - { - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - for (unsigned int i = 0; i < dim; i++) - { - if (q_point_list[q_point](i) < ele_origin[i]) - { - ele_origin[i] = q_point_list[q_point](i); - } - if (q_point_list[q_point](i) > ele_max[i]) - { - ele_max[i] = q_point_list[q_point](i); - } - } - } - } - - // Find a random point within the element - for (unsigned int j = 0; j < dim; j++) - { - randvec[j] = distr(gen); - nuc_ele_pos[j] = - ele_origin[j] + (ele_max[j] - ele_origin[j]) * randvec[j]; - } - - // Make sure point is in safety zone - bool insafetyzone = true; - for (unsigned int j = 0; j < dim; j++) - { - bool periodic_j = - (userInputs.BC_list[1].var_BC_type[2 * j] == PERIODIC); - bool insafetyzone_j = - (periodic_j || - ((nuc_ele_pos[j] > userInputs.get_no_nucleation_border_thickness( - variable_index)) && - (nuc_ele_pos[j] < - userInputs.domain_size[j] - - userInputs.get_no_nucleation_border_thickness( - variable_index)))); - insafetyzone = insafetyzone && insafetyzone_j; - } - - if (insafetyzone) - { - // Check to see if the order parameter anywhere within the - // element is above the threshold - bool anyqp_OK = false; - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - double sum_op = 0.0; - for (unsigned int var = 0; - var < userInputs.nucleation_need_value.size(); - var++) - { - for (unsigned int op = 0; - op < userInputs.nucleating_variable_indices.size(); - op++) - { - if (userInputs.nucleation_need_value[var] == - userInputs.nucleating_variable_indices[op]) - { - sum_op += var_values[var][q_point]; - } - } - } - if (sum_op < userInputs.nucleation_order_parameter_cutoff) - { - anyqp_OK = true; - } - } - - if (anyqp_OK) - { - // Pick the order parameter (not needed anymore since - // the probability is now calculated on a per OP - // basis) - /* - std::random_device rd2; - std::mt19937 gen2(rd2()); - std::uniform_int_distribution - int_distr(0,userInputs.nucleating_variable_indices.size()-1); - unsigned int op_for_nucleus = - userInputs.nucleating_variable_indices[int_distr(gen2)]; - std::cout << "Nucleation order parameter: " << - op_for_nucleus << " " << rand_val << std::endl; - */ - - // Add nucleus to prospective list - std::cout << "Prospective nucleation event. Nucleus no. " - << nuclei.size() + 1 << "\n"; - std::cout << "Nucleus center: " << nuc_ele_pos << "\n"; - std::cout << "Nucleus order parameter: " << variable_index - << "\n"; - auto *temp = new nucleus; - temp->index = nuclei.size(); - temp->center = nuc_ele_pos; - temp->semiaxes = - userInputs.get_nucleus_semiaxes(variable_index); - temp->seededTime = t; - temp->seedingTime = - userInputs.get_nucleus_hold_time(variable_index); - temp->seedingTimestep = inc; - temp->orderParameterIndex = variable_index; - newnuclei.push_back(*temp); - } - } - } - } - } - } -} - -// ======================================================================================================= -// Making sure all new nuclei from complete prospective list do not overlap with -// existing precipitates -// ======================================================================================================= -template -void -MatrixFreePDE::safetyCheckNewNuclei(std::vector> newnuclei, - std::vector &conflict_ids) -{ - // QGauss quadrature(degree+1); - QGaussLobatto quadrature(degree + 1); - FEValues fe_values(*(FESet[0]), - quadrature, - update_values | update_quadrature_points | update_JxW_values); - const unsigned int num_quad_points = quadrature.size(); - std::vector> op_values( - userInputs.nucleating_variable_indices.size(), - std::vector(num_quad_points)); - std::vector> q_point_list(num_quad_points); - - // Nucleus cycle - for (const auto &thisNucleus : newnuclei) - { - bool isClose = false; - - // Element cycle - - for (const auto &dof : dofHandlersSet_nonconst[0]->active_cell_iterators()) - { - if (dof->is_locally_owned()) - { - fe_values.reinit(dof); - for (unsigned int var = 0; - var < userInputs.nucleating_variable_indices.size(); - var++) - { - fe_values.get_function_values( - *(solutionSet[userInputs.nucleating_variable_indices[var]]), - op_values[var]); - } - q_point_list = fe_values.get_quadrature_points(); - - // Quadrature points cycle - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - // Calculate the ellipsoidal distance to the center of the - // nucleus - double weighted_dist = weightedDistanceFromNucleusCenter( - thisNucleus.center, - userInputs.get_nucleus_freeze_semiaxes( - thisNucleus.orderParameterIndex), - q_point_list[q_point], - thisNucleus.orderParameterIndex); - - if (weighted_dist < 1.0) - { - double sum_op = 0.0; - for (unsigned int num_op = 0; - num_op < userInputs.nucleating_variable_indices.size(); - num_op++) - { - sum_op += op_values[num_op][q_point]; - } - if (sum_op > 0.1) - { - isClose = true; - std::cout << "Attempted nucleation failed due to " - "overlap w/ existing particle!\n"; - conflict_ids.push_back(thisNucleus.index); - break; - } - } - } - if (isClose) - { - break; - } - } - } - } -} - -// ================================================================================= -// Refine mesh near the new nuclei -// ================================================================================= -template -void -MatrixFreePDE::refineMeshNearNuclei(std::vector> newnuclei) -{ - // QGauss quadrature(degree+1); - QGaussLobatto quadrature(degree + 1); - FEValues fe_values(*(FESet[0]), - quadrature, - update_values | update_quadrature_points | update_JxW_values); - const unsigned int num_quad_points = quadrature.size(); - std::vector> q_point_list(num_quad_points); - - typename Triangulation::active_cell_iterator ti; - - unsigned int numDoF_preremesh = totalDOFs; - - for (unsigned int remesh_index = 0; - remesh_index < (userInputs.max_refinement_level - userInputs.min_refinement_level); - remesh_index++) - { - ti = triangulation.begin_active(); - for (const auto &dof : dofHandlersSet_nonconst[0]->active_cell_iterators()) - { - if (dof->is_locally_owned()) - { - bool mark_refine = false; - - fe_values.reinit(dof); - q_point_list = fe_values.get_quadrature_points(); - - // Calculate the distance from the corner of the cell to the - // middle of the cell - double diag_dist = 0.0; - for (unsigned int i = 0; i < dim; i++) - { - diag_dist += (userInputs.domain_size[i] * userInputs.domain_size[i]) / - (userInputs.subdivisions[i] * userInputs.subdivisions[i]); - } - diag_dist = sqrt(diag_dist); - diag_dist /= 2.0 * pow(2.0, ti->level()); - - for (unsigned int q_point = 0; q_point < num_quad_points; ++q_point) - { - for (const auto &thisNucleus : newnuclei) - { - // Calculate the ellipsoidal distance to the center of the - // nucleus - double weighted_dist = weightedDistanceFromNucleusCenter( - thisNucleus.center, - userInputs.get_nucleus_freeze_semiaxes( - thisNucleus.orderParameterIndex), - q_point_list[q_point], - thisNucleus.orderParameterIndex); - - if (weighted_dist < 1.0 || - thisNucleus.center.distance(q_point_list[q_point]) < diag_dist) - { - if ((unsigned int) ti->level() < - userInputs.max_refinement_level) - { - mark_refine = true; - break; - } - } - if (mark_refine) - { - break; - } - } - if (mark_refine) - { - break; - } - } - if (mark_refine) - { - dof->set_refine_flag(); - } - } - ++ti; - } - // The bulk of all of modifySolutionFields is spent in the following two function - // calls - AMR.refine_grid(); - reinit(); - - // If the mesh hasn't changed from the previous cycle, stop remeshing - if (totalDOFs == numDoF_preremesh) - { - break; - } - numDoF_preremesh = totalDOFs; - } -} - -// First of two versions of this function to calculated the weighted distance -// from the center of a nucleus_semiaxes This version is for when the points are -// given as doubles -template -double -MatrixFreePDE::weightedDistanceFromNucleusCenter( - const dealii::Point center, - const std::vector &semiaxes, - const dealii::Point q_point_loc, - const unsigned int var_index) const -{ - double weighted_dist = 0.0; - dealii::Tensor<1, dim, double> shortest_edist_tensor = center - q_point_loc; - for (unsigned int i = 0; i < dim; i++) - { - if (userInputs.BC_list[var_index].var_BC_type[2 * i] == PERIODIC) - { - shortest_edist_tensor[i] = - shortest_edist_tensor[i] - - round(shortest_edist_tensor[i] / userInputs.domain_size[i]) * - userInputs.domain_size[i]; - } - } - shortest_edist_tensor = - userInputs.get_nucleus_rotation_matrix(var_index) * shortest_edist_tensor; - for (unsigned int i = 0; i < dim; i++) - { - shortest_edist_tensor[i] /= semiaxes[i]; - } - weighted_dist = shortest_edist_tensor.norm(); - return weighted_dist; -} - -// Second of two versions of this function to calculated the weighted distance -// from the center of a nucleus_semiaxes This version is for when the points are -// given as vectorized arrays -template -dealii::VectorizedArray -MatrixFreePDE::weightedDistanceFromNucleusCenter( - const dealii::Point center, - const std::vector &semiaxes, - const dealii::Point> q_point_loc, - const unsigned int var_index) const -{ - dealii::VectorizedArray weighted_dist = constV(0.0); - dealii::Tensor<1, dim, dealii::VectorizedArray> shortest_edist_tensor; - for (unsigned int j = 0; j < dim; j++) - { - shortest_edist_tensor[j] = - center(j) - q_point_loc(j); // Can I do this outside the loop? - - if (userInputs.BC_list[var_index].var_BC_type[2 * j] == PERIODIC) - { - for (unsigned k = 0; k < q_point_loc(0).size(); k++) - { - shortest_edist_tensor[j][k] = - shortest_edist_tensor[j][k] - - round(shortest_edist_tensor[j][k] / userInputs.domain_size[j]) * - userInputs.domain_size[j]; - } - } - } - shortest_edist_tensor = - userInputs.get_nucleus_rotation_matrix(var_index) * shortest_edist_tensor; - for (unsigned int j = 0; j < dim; j++) - { - shortest_edist_tensor[j] /= constV(semiaxes[j]); - } - weighted_dist = shortest_edist_tensor.norm_square(); - for (unsigned k = 0; k < q_point_loc(0).size(); k++) - { - weighted_dist[k] = sqrt(weighted_dist[k]); - } - return weighted_dist; -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/src/nucleation/parallelNucleationList.cc b/src/nucleation/parallelNucleationList.cc deleted file mode 100644 index af0497847..000000000 --- a/src/nucleation/parallelNucleationList.cc +++ /dev/null @@ -1,594 +0,0 @@ -#include -#include - -#include -#include - -// ================================================================================= -// Constructor -// ================================================================================= -template -parallelNucleationList::parallelNucleationList(std::vector> _newnuclei) - : newnuclei(_newnuclei) -{} - -// ================================================================================= -// Generate global list of new nuclei and resolve conflicts between new nuclei -// ================================================================================= -template -std::vector> -parallelNucleationList::buildGlobalNucleiList(double min_dist_between_nuclei, - unsigned int old_num_nuclei) -{ - // MPI INITIALIZATON - const unsigned int numProcs = dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD); - const unsigned int thisProc = dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD); - if (numProcs > 1) - { - // Cycle through each processor, sending and receiving, to append the list - // of new nuclei - for (unsigned int proc_index = 0; proc_index < numProcs - 1; proc_index++) - { - if (thisProc == proc_index) - { - sendUpdate(thisProc + 1); - } - else if (thisProc == proc_index + 1) - { - receiveUpdate(thisProc - 1); - } - MPI_Barrier(MPI_COMM_WORLD); - } - // The final processor now has all of the new nucleation attempts - // Check for conflicts on the final processor before broadcasting the list - if (thisProc == numProcs - 1) - { - resolveNucleationConflicts(min_dist_between_nuclei, old_num_nuclei); - } - - // The final processor now has the final list of the new nuclei, broadcast - // it to all the other processors - broadcastUpdate(numProcs - 1, thisProc); - } - else - { - // Check for conflicts between nucleation attempts this time step - resolveNucleationConflicts(min_dist_between_nuclei, old_num_nuclei); - } - - return newnuclei; -} - -// ================================================================================= -// Sends the list of new nuclei to the next processor -// ================================================================================= -template -void -parallelNucleationList::sendUpdate(int procno) const -{ - int currnonucs = newnuclei.size(); - // MPI SECTION TO SEND INFORMATION TO THE PROCESSOR procno - // Sending local no. of nuclei - MPI_Send(&currnonucs, 1, MPI_INT, procno, 0, MPI_COMM_WORLD); - if (currnonucs > 0) - { - // Creating vectors of each quantity in nuclei. Each numbered acording to - // the tags used for MPI_Send/MPI_Recv 1 - index - std::vector s_index; - // 2 - "x" componenet of center - std::vector s_center_x; - // 3 - "y" componenet of center - std::vector s_center_y; - // 4 - "z" componenet of center - std::vector s_center_z; - // 5 - radius - std::vector s_semiaxis_a; - std::vector s_semiaxis_b; - std::vector s_semiaxis_c; - // 6 - seededTime - std::vector s_seededTime; - // 7 - seedingTime - std::vector s_seedingTime; - // 8 - seedingTimestep - std::vector s_seedingTimestep; - // 9 - orderParameterIndex - std::vector s_orderParameterIndex; - - // Loop to store info of all nuclei into vectors - for (const auto &thisNuclei : newnuclei) - { - s_index.push_back(thisNuclei.index); - dealii::Point s_center = thisNuclei.center; - s_center_x.push_back(s_center[0]); - s_center_y.push_back(s_center[1]); - if (dim == 3) - { - s_center_z.push_back(s_center[2]); - } - - s_semiaxis_a.push_back(thisNuclei.semiaxes[0]); - s_semiaxis_b.push_back(thisNuclei.semiaxes[1]); - if (dim == 3) - { - s_semiaxis_c.push_back(thisNuclei.semiaxes[2]); - } - - s_seededTime.push_back(thisNuclei.seededTime); - s_seedingTime.push_back(thisNuclei.seedingTime); - s_seedingTimestep.push_back(thisNuclei.seedingTimestep); - s_orderParameterIndex.push_back(thisNuclei.orderParameterIndex); - } - // Send vectors to next processor - MPI_Send(s_index.data(), currnonucs, MPI_UNSIGNED, procno, 1, MPI_COMM_WORLD); - MPI_Send(s_center_x.data(), currnonucs, MPI_DOUBLE, procno, 2, MPI_COMM_WORLD); - MPI_Send(s_center_y.data(), currnonucs, MPI_DOUBLE, procno, 3, MPI_COMM_WORLD); - if (dim == 3) - { - MPI_Send(s_center_z.data(), currnonucs, MPI_DOUBLE, procno, 4, MPI_COMM_WORLD); - MPI_Send(s_semiaxis_c.data(), - currnonucs, - MPI_DOUBLE, - procno, - 7, - MPI_COMM_WORLD); - } - - MPI_Send(s_semiaxis_a.data(), currnonucs, MPI_DOUBLE, procno, 5, MPI_COMM_WORLD); - MPI_Send(s_semiaxis_b.data(), currnonucs, MPI_DOUBLE, procno, 6, MPI_COMM_WORLD); - - MPI_Send(s_seededTime.data(), currnonucs, MPI_DOUBLE, procno, 8, MPI_COMM_WORLD); - MPI_Send(s_seedingTime.data(), currnonucs, MPI_DOUBLE, procno, 9, MPI_COMM_WORLD); - MPI_Send(s_seedingTimestep.data(), - currnonucs, - MPI_UNSIGNED, - procno, - 10, - MPI_COMM_WORLD); - MPI_Send(s_orderParameterIndex.data(), - currnonucs, - MPI_UNSIGNED, - procno, - 11, - MPI_COMM_WORLD); - } - // END OF MPI SECTION -} - -// ================================================================================= -// Recieves the list of new nuclei to the next processor -// ================================================================================= -template -void -parallelNucleationList::receiveUpdate(int procno) -{ - // MPI PROCEDURE TO RECIEVE INFORMATION FROM ANOTHER PROCESSOR AND UPDATE - // LOCAL NUCLEI INFORMATION - int recvnonucs = 0; - MPI_Recv(&recvnonucs, 1, MPI_INT, procno, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - if (recvnonucs > 0) - { - // Creating vectors of each quantity in nuclei. Each numbered acording to - // the tags used for MPI_Send/MPI_Recv 1 - index - std::vector r_index(recvnonucs, 0); - // 2 - "x" componenet of center - std::vector r_center_x(recvnonucs, 0.0); - // 3 - "y" componenet of center - std::vector r_center_y(recvnonucs, 0.0); - // 4 - "z" componenet of center - std::vector r_center_z(recvnonucs, 0.0); - // 5 - semiaxes - std::vector r_semiaxis_a(recvnonucs, 0.0); - std::vector r_semiaxis_b(recvnonucs, 0.0); - std::vector r_semiaxis_c(recvnonucs, 0.0); - // 6 - seededTime - std::vector r_seededTime(recvnonucs, 0.0); - // 7 - seedingTime - std::vector r_seedingTime(recvnonucs, 0.0); - // 8 - seedingTimestep - std::vector r_seedingTimestep(recvnonucs, 0); - // 9 - orderParameterIndex - std::vector r_orderParameterIndex(recvnonucs, 0); - - // Recieve vectors from processor procno - MPI_Recv(r_index.data(), - recvnonucs, - MPI_UNSIGNED, - procno, - 1, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - MPI_Recv(r_center_x.data(), - recvnonucs, - MPI_DOUBLE, - procno, - 2, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - MPI_Recv(r_center_y.data(), - recvnonucs, - MPI_DOUBLE, - procno, - 3, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - if (dim == 3) - { - MPI_Recv(r_center_z.data(), - recvnonucs, - MPI_DOUBLE, - procno, - 4, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - MPI_Recv(r_semiaxis_c.data(), - recvnonucs, - MPI_DOUBLE, - procno, - 7, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - } - - MPI_Recv(r_semiaxis_a.data(), - recvnonucs, - MPI_DOUBLE, - procno, - 5, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - MPI_Recv(r_semiaxis_b.data(), - recvnonucs, - MPI_DOUBLE, - procno, - 6, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - - MPI_Recv(r_seededTime.data(), - recvnonucs, - MPI_DOUBLE, - procno, - 8, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - MPI_Recv(r_seedingTime.data(), - recvnonucs, - MPI_DOUBLE, - procno, - 9, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - MPI_Recv(r_seedingTimestep.data(), - recvnonucs, - MPI_UNSIGNED, - procno, - 10, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - MPI_Recv(r_orderParameterIndex.data(), - recvnonucs, - MPI_UNSIGNED, - procno, - 11, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - - // Loop to store info in vectors onto the nuclei structure - for (int jnuc = 0; jnuc <= recvnonucs - 1; jnuc++) - { - auto temp = std::make_unique>(); - - temp->index = r_index[jnuc]; - dealii::Point r_center; - r_center[0] = r_center_x[jnuc]; - r_center[1] = r_center_y[jnuc]; - if (dim == 3) - { - r_center[2] = r_center_z[jnuc]; - } - temp->center = r_center; - temp->semiaxes.push_back(r_semiaxis_a[jnuc]); - temp->semiaxes.push_back(r_semiaxis_b[jnuc]); - if (dim == 3) - { - temp->semiaxes.push_back(r_semiaxis_c[jnuc]); - } - temp->seededTime = r_seededTime[jnuc]; - temp->seedingTime = r_seedingTime[jnuc]; - temp->seedingTimestep = r_seedingTimestep[jnuc]; - temp->orderParameterIndex = r_orderParameterIndex[jnuc]; - newnuclei.push_back(*temp); - } - } -} - -// ================================================================================= -// Broadcast the final list of new nuclei from the last processor to the rest -// ================================================================================= -template -void -parallelNucleationList::broadcastUpdate(int broadcastProc, int thisProc) -{ - // MPI PROCEDURE TO SEND THE LIST OF NEW NUCLEI FROM ONE PROCESSOR TO ALL THE - // OTHERS - int currnonucs = newnuclei.size(); - MPI_Bcast(&currnonucs, 1, MPI_INT, broadcastProc, MPI_COMM_WORLD); - - if (currnonucs == 0) - { - return; - } - - // Creating vectors of each quantity in nuclei. Each numbered acording to - // the tags used for MPI_Send/MPI_Recv - unsigned int initial_vec_size = 0; - if (thisProc == broadcastProc) - { - initial_vec_size = 0; - } - else - { - initial_vec_size = currnonucs; - } - - // 1 - index - std::vector r_index(initial_vec_size, 0); - // 2 - "x" componenet of center - std::vector r_center_x(initial_vec_size, 0.0); - // 3 - "y" componenet of center - std::vector r_center_y(initial_vec_size, 0.0); - // 4 - "z" componenet of center - std::vector r_center_z(initial_vec_size, 0.0); - // 5 - radius - std::vector r_semiaxis_a(initial_vec_size, 0.0); - std::vector r_semiaxis_b(initial_vec_size, 0.0); - std::vector r_semiaxis_c(initial_vec_size, 0.0); - // 6 - seededTime - std::vector r_seededTime(initial_vec_size, 0.0); - // 7 - seedingTime - std::vector r_seedingTime(initial_vec_size, 0.0); - // 8 - seedingTimestep - std::vector r_seedingTimestep(initial_vec_size, 0); - // 9 - orderParameterIndex - std::vector r_orderParameterIndex(initial_vec_size, 0); - - if (thisProc == broadcastProc) - { - for (const auto &thisNuclei : newnuclei) - { - r_index.push_back(thisNuclei.index); - dealii::Point s_center = thisNuclei.center; - r_center_x.push_back(s_center[0]); - r_center_y.push_back(s_center[1]); - if (dim == 3) - { - r_center_z.push_back(s_center[2]); - } - - r_semiaxis_a.push_back(thisNuclei.semiaxes[0]); - r_semiaxis_b.push_back(thisNuclei.semiaxes[1]); - if (dim == 3) - { - r_semiaxis_c.push_back(thisNuclei.semiaxes[2]); - } - - r_seededTime.push_back(thisNuclei.seededTime); - r_seedingTime.push_back(thisNuclei.seedingTime); - r_seedingTimestep.push_back(thisNuclei.seedingTimestep); - r_orderParameterIndex.push_back(thisNuclei.orderParameterIndex); - } - } - - // Recieve vectors from processor procno - MPI_Bcast(r_index.data(), currnonucs, MPI_UNSIGNED, broadcastProc, MPI_COMM_WORLD); - MPI_Bcast(r_center_x.data(), currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD); - MPI_Bcast(r_center_y.data(), currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD); - if (dim == 3) - { - MPI_Bcast(r_center_z.data(), currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD); - } - MPI_Bcast(r_semiaxis_a.data(), currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD); - MPI_Bcast(r_semiaxis_b.data(), currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD); - if (dim == 3) - { - MPI_Bcast(r_semiaxis_c.data(), - currnonucs, - MPI_DOUBLE, - broadcastProc, - MPI_COMM_WORLD); - } - MPI_Bcast(r_seededTime.data(), currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD); - MPI_Bcast(r_seedingTime.data(), currnonucs, MPI_DOUBLE, broadcastProc, MPI_COMM_WORLD); - MPI_Bcast(r_seedingTimestep.data(), - currnonucs, - MPI_UNSIGNED, - broadcastProc, - MPI_COMM_WORLD); - MPI_Bcast(r_orderParameterIndex.data(), - currnonucs, - MPI_UNSIGNED, - broadcastProc, - MPI_COMM_WORLD); - - newnuclei.clear(); - - // Loop to store info in vectors onto the nuclei structure - for (int jnuc = 0; jnuc <= currnonucs - 1; jnuc++) - { - auto temp = std::make_unique>(); - - temp->index = r_index[jnuc]; - dealii::Point r_center; - r_center[0] = r_center_x[jnuc]; - r_center[1] = r_center_y[jnuc]; - if (dim == 3) - { - r_center[2] = r_center_z[jnuc]; - } - temp->center = r_center; - temp->semiaxes.push_back(r_semiaxis_a[jnuc]); - temp->semiaxes.push_back(r_semiaxis_b[jnuc]); - if (dim == 3) - { - temp->semiaxes.push_back(r_semiaxis_c[jnuc]); - } - temp->seededTime = r_seededTime[jnuc]; - temp->seedingTime = r_seedingTime[jnuc]; - temp->seedingTimestep = r_seedingTimestep[jnuc]; - temp->orderParameterIndex = r_orderParameterIndex[jnuc]; - newnuclei.push_back(*temp); - } -} - -// ================================================================================= -// Determine if any new nuclei are in conflict and resolve those conflicts -// ================================================================================= -template -void -parallelNucleationList::resolveNucleationConflicts(double min_dist_between_nuclei, - unsigned int old_num_nuclei) -{ - std::vector> newnuclei_cleaned; - - for (unsigned int nuc_index = 0; nuc_index < newnuclei.size(); nuc_index++) - { - bool isClose = false; - - for (unsigned int prev_nuc_index = 0; prev_nuc_index < nuc_index; prev_nuc_index++) - { - // We may want to break this section into a separate function to allow - // different choices for when nucleation should be prevented - if (newnuclei[nuc_index].center.distance(newnuclei[prev_nuc_index].center) < - min_dist_between_nuclei) - { - isClose = true; - std::cout << "Conflict between nuclei! Distance is: " - << newnuclei[nuc_index].center.distance( - newnuclei[prev_nuc_index].center) - << " Conflict removed." << std::endl; - break; - } - } - - if (!isClose) - { - newnuclei[nuc_index].index = old_num_nuclei + newnuclei_cleaned.size(); - newnuclei_cleaned.push_back(newnuclei[nuc_index]); - } - } - - newnuclei = newnuclei_cleaned; -} - -// ================================================================================= -// Remove nuclei from the list of nuclei given a local list of nucleus indices -// ================================================================================= -template -std::vector> -parallelNucleationList::removeSubsetOfNuclei( - std::vector nuclei_to_remove, - unsigned int nuclei_size) -{ - // Note: This method is very similar to buildGlobalNucleiList in structure, - // and uses simplified versions of what is done in sendUpdate, receiveUpdate, - // and broadcastUpdate. There is likely a cleaner way to reorganize the - // methods to reduce duplication. - - // MPI INITIALIZATON - const int numProcs = - static_cast(dealii::Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD)); - const int thisProc = - static_cast(dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)); - - // Build a global list of nuclei to delete, first sending the length of the - // vector of indices, then the vector itself - if (numProcs > 1) - { - // Cycle through each processor, sending and receiving, to append the list - // of new nuclei - for (int proc_index = 0; proc_index < numProcs - 1; proc_index++) - { - if (thisProc == proc_index) - { - int currnonucs = static_cast(nuclei_to_remove.size()); - MPI_Send(&currnonucs, 1, MPI_INT, thisProc + 1, 0, MPI_COMM_WORLD); - MPI_Send(nuclei_to_remove.data(), - currnonucs, - MPI_UNSIGNED, - thisProc + 1, - 1, - MPI_COMM_WORLD); - } - else if (thisProc == proc_index + 1) - { - int recvnonucs = 0; - MPI_Recv(&recvnonucs, - 1, - MPI_INT, - thisProc - 1, - 0, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - std::vector recieved_nuclei_to_remove(recvnonucs); - MPI_Recv(recieved_nuclei_to_remove.data(), - recvnonucs, - MPI_UNSIGNED, - thisProc - 1, - 1, - MPI_COMM_WORLD, - MPI_STATUS_IGNORE); - nuclei_to_remove.insert(nuclei_to_remove.end(), - recieved_nuclei_to_remove.begin(), - recieved_nuclei_to_remove.end()); - } - MPI_Barrier(MPI_COMM_WORLD); - } - - // The final processor now has the final list of the new nuclei, broadcast - // it to all the other processors - int currnonucs = static_cast(nuclei_to_remove.size()); - MPI_Bcast(&currnonucs, 1, MPI_INT, numProcs - 1, MPI_COMM_WORLD); - std::vector recieved_nuclei_to_remove(currnonucs); - if (thisProc == numProcs - 1) - { - recieved_nuclei_to_remove = nuclei_to_remove; - } - MPI_Bcast(recieved_nuclei_to_remove.data(), - currnonucs, - MPI_UNSIGNED, - numProcs - 1, - MPI_COMM_WORLD); - nuclei_to_remove = recieved_nuclei_to_remove; - } - - for (const unsigned int nuclei : nuclei_to_remove) - { - std::cout << thisProc << ": " << nuclei << "\n"; - } - - // Remove the nuclei from the list - std::vector> pruned_list; - for (unsigned int nuc = 0; nuc < newnuclei.size(); nuc++) - { - bool pruneNucleus = false; - for (const unsigned int nuclei : nuclei_to_remove) - { - if (nuclei == nuclei_size + nuc) - { - pruneNucleus = true; - break; - } - } - if (!pruneNucleus) - { - pruned_list.push_back(newnuclei[nuc]); - } - } - return pruned_list; -} - -// ================================================================================= -// Template instantiations -// ================================================================================= -template class parallelNucleationList<2>; -template class parallelNucleationList<3>; diff --git a/src/utilities/CMakeLists.txt b/src/utilities/CMakeLists.txt index be0c80f31..e8104c5d4 100644 --- a/src/utilities/CMakeLists.txt +++ b/src/utilities/CMakeLists.txt @@ -1,5 +1,4 @@ # Manually specify files to be included list(APPEND PRISMS_PF_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/utilities.cc ) set(PRISMS_PF_SOURCE_FILES ${PRISMS_PF_SOURCE_FILES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/utilities/utilities.cc b/src/utilities/utilities.cc deleted file mode 100644 index 81ee5234a..000000000 --- a/src/utilities/utilities.cc +++ /dev/null @@ -1,37 +0,0 @@ -// utility functions for the MatrixFreePDE class - -#include - -// return index of given field name if exists, else throw error -template -unsigned int -MatrixFreePDE::getFieldIndex(std::string _name) -{ - for (const auto &field : fields) - { - if (field.name.compare(_name) == 0) - { - return field.index; - } - } - pcout << "\nutilities.h: field '" << _name.c_str() << "' not initialized\n"; - exit(-1); -} - -template class MatrixFreePDE<2, 1>; -template class MatrixFreePDE<3, 1>; - -template class MatrixFreePDE<2, 2>; -template class MatrixFreePDE<3, 2>; - -template class MatrixFreePDE<3, 3>; -template class MatrixFreePDE<2, 3>; - -template class MatrixFreePDE<3, 4>; -template class MatrixFreePDE<2, 4>; - -template class MatrixFreePDE<3, 5>; -template class MatrixFreePDE<2, 5>; - -template class MatrixFreePDE<3, 6>; -template class MatrixFreePDE<2, 6>; \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e0dc0eb85..3171ae7b3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,8 +5,6 @@ cmake_minimum_required(VERSION 3.8.0) -project(prisms_pf_unit_tests) - # ========================================================= # Some basic bookkeeping # ========================================================= @@ -54,6 +52,8 @@ endif() deal_ii_initialize_cached_variables() +project(prisms_pf_unit_tests) + # Create compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(FORCE_COLORED_OUTPUT ON CACHE BOOL "Forces colored output when compiling with gcc and clang.") diff --git a/tests/core/nonlinearity.cc b/tests/core/field_solve_type.cc similarity index 88% rename from tests/core/nonlinearity.cc rename to tests/core/field_solve_type.cc index 55c28446e..e7ae14371 100644 --- a/tests/core/nonlinearity.cc +++ b/tests/core/field_solve_type.cc @@ -1,14 +1,15 @@ #include "catch.hpp" -#include "core/varTypeEnums.h" +#include "core/type_enums.h" -#include +#include #include /** - * This unit test looks at variableAttributes.h and variableAttributeLoader.h and the - * parser determines how each field is related to one another for nonlinearity. + * This unit test looks at variable_attributes.h and variable_attribute_loader.h and how + * the parser determines the field sovle type of each field. In turn, the determines the + * solver and other information such as nonlinearity. */ -TEST_CASE("Nonlinearity criterion") +TEST_CASE("Field solve types") { /** * Two nonlinear equations based on the steady-state version of the Cahn-Hilliard @@ -63,7 +64,8 @@ TEST_CASE("Nonlinearity criterion") REQUIRE(pp_variables.empty()); for (unsigned int index : {0, 1}) { - REQUIRE(main_variables.at(index).is_nonlinear); + REQUIRE(main_variables.at(index).field_solve_type == + fieldSolveType::NONEXPLICIT_CO_NONLINEAR); } } @@ -78,7 +80,7 @@ TEST_CASE("Nonlinearity criterion") * weak form this becomes * ∇w⋅(∇u/√(1+|∇u|^2))+∇w⋅(δ∇u/√(1+|∇u|^2))-∇w⋅((∇u⋅δ∇u)∇u/√(1+|∇u|^2))=0. * - * The third equuation is a simple Poisson equation Δu=0. + * The third equation is a simple Poisson equation Δu=0. * * The fourth equation is the steady-state version of the Allen-Cahn equation * u^3-u-γΔu=0. In the weak form with γ=1 this becomes w(u^3+δu^3)+w(u-δu)+∇w(∇u+δ∇u)=0. @@ -146,11 +148,13 @@ TEST_CASE("Nonlinearity criterion") REQUIRE(pp_variables.empty()); for (unsigned int index : {0, 1}) { - REQUIRE(main_variables.at(index).is_nonlinear); + REQUIRE(main_variables.at(index).field_solve_type == + fieldSolveType::NONEXPLICIT_SELF_NONLINEAR); } for (unsigned int index : {2, 3}) { - REQUIRE(!main_variables.at(index).is_nonlinear); + REQUIRE(main_variables.at(index).field_solve_type == + fieldSolveType::NONEXPLICIT_LINEAR); } } @@ -193,7 +197,7 @@ TEST_CASE("Nonlinearity criterion") REQUIRE(pp_variables.empty()); for (unsigned int index : {0, 1, 2, 3}) { - REQUIRE(!main_variables.at(index).is_nonlinear); + REQUIRE(main_variables.at(index).field_solve_type == fieldSolveType::EXPLICIT); } } @@ -245,9 +249,14 @@ TEST_CASE("Nonlinearity criterion") REQUIRE(main_variables.size() == 4); REQUIRE(pp_variables.empty()); - for (unsigned int index : {0, 1, 2, 3}) + for (unsigned int index : {0, 1}) + { + REQUIRE(main_variables.at(index).field_solve_type == fieldSolveType::EXPLICIT); + } + for (unsigned int index : {2, 3}) { - REQUIRE(!main_variables.at(index).is_nonlinear); + REQUIRE(main_variables.at(index).field_solve_type == + fieldSolveType::NONEXPLICIT_AUXILIARY); } } @@ -310,10 +319,14 @@ TEST_CASE("Nonlinearity criterion") REQUIRE(main_variables.size() == 4); REQUIRE(pp_variables.empty()); - for (unsigned int index : {0, 1, 2}) + for (unsigned int index : {0, 1}) + { + REQUIRE(main_variables.at(index).field_solve_type == fieldSolveType::EXPLICIT); + } + for (unsigned int index : {2, 3}) { - REQUIRE(!main_variables.at(index).is_nonlinear); + REQUIRE(main_variables.at(index).field_solve_type == + fieldSolveType::NONEXPLICIT_LINEAR); } - REQUIRE(main_variables.at(3).is_nonlinear); } } \ No newline at end of file diff --git a/tests/core/fields.cc b/tests/core/fields.cc deleted file mode 100644 index 7ad3bd081..000000000 --- a/tests/core/fields.cc +++ /dev/null @@ -1,155 +0,0 @@ -#include "catch.hpp" - -#include - -/** - * This unit test looks at fields.h and the initialization of various field types (PDEtype - * & dim & spacedim). For successive objects of the same template parameters, the field - * index should increase by 1. An error should be thrown for invalid enum types. - */ -TEST_CASE("Field declarations") -{ - const std::string field_name = "phi"; - - SECTION("SCALAR") - { - SECTION("EXPLICIT_TIME_DEPENDENT") - { - Field<1> field_1d(SCALAR, EXPLICIT_TIME_DEPENDENT, field_name); - Field<2> field_2d(SCALAR, EXPLICIT_TIME_DEPENDENT, field_name); - Field<3> field_3d(SCALAR, EXPLICIT_TIME_DEPENDENT, field_name); - - REQUIRE(field_1d.numComponents == 1); - REQUIRE(field_1d.index == 0); - - REQUIRE(field_2d.numComponents == 1); - REQUIRE(field_2d.index == 0); - - REQUIRE(field_3d.numComponents == 1); - REQUIRE(field_3d.index == 0); - } - SECTION("IMPLICIT_TIME_DEPENDENT") - { - Field<1> field_1d(SCALAR, IMPLICIT_TIME_DEPENDENT, field_name); - Field<2> field_2d(SCALAR, IMPLICIT_TIME_DEPENDENT, field_name); - Field<3> field_3d(SCALAR, IMPLICIT_TIME_DEPENDENT, field_name); - - REQUIRE(field_1d.numComponents == 1); - REQUIRE(field_1d.index == 1); - - REQUIRE(field_2d.numComponents == 1); - REQUIRE(field_2d.index == 1); - - REQUIRE(field_3d.numComponents == 1); - REQUIRE(field_3d.index == 1); - } - SECTION("TIME_INDEPENDENT") - { - Field<1> field_1d(SCALAR, TIME_INDEPENDENT, field_name); - Field<2> field_2d(SCALAR, TIME_INDEPENDENT, field_name); - Field<3> field_3d(SCALAR, TIME_INDEPENDENT, field_name); - - REQUIRE(field_1d.numComponents == 1); - REQUIRE(field_1d.index == 2); - - REQUIRE(field_2d.numComponents == 1); - REQUIRE(field_2d.index == 2); - - REQUIRE(field_3d.numComponents == 1); - REQUIRE(field_3d.index == 2); - } - SECTION("AUXILIARY") - { - Field<1> field_1d(SCALAR, AUXILIARY, field_name); - Field<2> field_2d(SCALAR, AUXILIARY, field_name); - Field<3> field_3d(SCALAR, AUXILIARY, field_name); - - REQUIRE(field_1d.numComponents == 1); - REQUIRE(field_1d.index == 3); - - REQUIRE(field_2d.numComponents == 1); - REQUIRE(field_2d.index == 3); - - REQUIRE(field_3d.numComponents == 1); - REQUIRE(field_3d.index == 3); - } - } - SECTION("VECTOR") - { - SECTION("EXPLICIT_TIME_DEPENDENT") - { - Field<1> field_1d(VECTOR, EXPLICIT_TIME_DEPENDENT, field_name); - Field<2> field_2d(VECTOR, EXPLICIT_TIME_DEPENDENT, field_name); - Field<3> field_3d(VECTOR, EXPLICIT_TIME_DEPENDENT, field_name); - - REQUIRE(field_1d.numComponents == 1); - REQUIRE(field_1d.index == 4); - - REQUIRE(field_2d.numComponents == 2); - REQUIRE(field_2d.index == 4); - - REQUIRE(field_3d.numComponents == 3); - REQUIRE(field_3d.index == 4); - } - SECTION("IMPLICIT_TIME_DEPENDENT") - { - Field<1> field_1d(VECTOR, IMPLICIT_TIME_DEPENDENT, field_name); - Field<2> field_2d(VECTOR, IMPLICIT_TIME_DEPENDENT, field_name); - Field<3> field_3d(VECTOR, IMPLICIT_TIME_DEPENDENT, field_name); - - REQUIRE(field_1d.numComponents == 1); - REQUIRE(field_1d.index == 5); - - REQUIRE(field_2d.numComponents == 2); - REQUIRE(field_2d.index == 5); - - REQUIRE(field_3d.numComponents == 3); - REQUIRE(field_3d.index == 5); - } - SECTION("TIME_INDEPENDENT") - { - Field<1> field_1d(VECTOR, TIME_INDEPENDENT, field_name); - Field<2> field_2d(VECTOR, TIME_INDEPENDENT, field_name); - Field<3> field_3d(VECTOR, TIME_INDEPENDENT, field_name); - - REQUIRE(field_1d.numComponents == 1); - REQUIRE(field_1d.index == 6); - - REQUIRE(field_2d.numComponents == 2); - REQUIRE(field_2d.index == 6); - - REQUIRE(field_3d.numComponents == 3); - REQUIRE(field_3d.index == 6); - } - SECTION("AUXILIARY") - { - Field<1> field_1d(VECTOR, AUXILIARY, field_name); - Field<2> field_2d(VECTOR, AUXILIARY, field_name); - Field<3> field_3d(VECTOR, AUXILIARY, field_name); - - REQUIRE(field_1d.numComponents == 1); - REQUIRE(field_1d.index == 7); - - REQUIRE(field_2d.numComponents == 2); - REQUIRE(field_2d.index == 7); - - REQUIRE(field_3d.numComponents == 3); - REQUIRE(field_3d.index == 7); - } - } - SECTION("Invalid field type") - { - const auto invalid_field_type = static_cast(-1); - - REQUIRE_THROWS_AS(Field<3>(invalid_field_type, EXPLICIT_TIME_DEPENDENT, field_name), - std::invalid_argument); - } - - SECTION("Invalid PDE type") - { - const auto invalid_PDE_type = static_cast(-1); - - REQUIRE_THROWS_AS(Field<3>(SCALAR, invalid_PDE_type, field_name), - std::invalid_argument); - } -} \ No newline at end of file diff --git a/tests/field_input/field_input.cc b/tests/field_input/field_input.cc index 6d5ff2775..e69de29bb 100644 --- a/tests/field_input/field_input.cc +++ b/tests/field_input/field_input.cc @@ -1,64 +0,0 @@ -#include "catch.hpp" - -#include - -TEST_CASE("Unstructured vtk File read-in") -{ - SECTION("2D") - { - using ScalarField = PRISMS::PField; - using Body = PRISMS::Body; - Body body; - - REQUIRE_THROWS_AS(body.read_vtk("invalid"), std::runtime_error); - - body.read_vtk("field_input/test_2D_1degree.vtk"); - - ScalarField n_field = body.find_scalar_field("n"); - ScalarField nx_field = body.find_scalar_field("|nx|"); - - for (auto x : {0.0, 5.0, 10.0}) - { - for (auto y : {0.0, 5.0, 10.0}) - { - double point[2] = {x, y}; - REQUIRE(n_field(point) == x); - REQUIRE(nx_field(point) == 1.0); - } - } - - REQUIRE_THROWS_AS(body.find_scalar_field("invalid"), std::invalid_argument); - } - - SECTION("3D") - { - using ScalarField = PRISMS::PField; - using Body = PRISMS::Body; - Body body; - - REQUIRE_THROWS_AS(body.read_vtk("invalid"), std::runtime_error); - - body.read_vtk("field_input/test_3D_1degree.vtk"); - - ScalarField n_field = body.find_scalar_field("n"); - ScalarField nx_field = body.find_scalar_field("|nx|"); - - for (auto x : {0.0, 5.0, 10.0}) - { - for (auto y : {0.0, 5.0, 10.0}) - { - for (auto z : {0.0, 5.0, 10.0}) - { - double point[3] = {x, y, z}; - REQUIRE(n_field(point) == x); - REQUIRE(nx_field(point) == 1.0); - } - } - } - - REQUIRE_THROWS_AS(body.find_scalar_field("invalid"), std::invalid_argument); - } -} - -TEST_CASE("Rectilinear vtk File read-in") -{} \ No newline at end of file