|
| 1 | +// SPDX-FileCopyrightText: © 2025 PRISMS Center at the University of Michigan |
| 2 | +// SPDX-License-Identifier: GNU Lesser General Public Version 2.1 |
| 3 | + |
| 4 | +#include <prismspf/core/pde_operator_base.h> |
| 5 | + |
| 6 | +#include <prismspf/solvers/solver_base.h> |
| 7 | + |
| 8 | +#include <prismspf/utilities/integrator.h> |
| 9 | + |
| 10 | +#include <cmath> |
| 11 | + |
| 12 | +PRISMS_PF_BEGIN_NAMESPACE |
| 13 | + |
| 14 | +template <unsigned int dim, unsigned int degree, typename number> |
| 15 | +class CustomPDE : public PDEOperatorBase<dim, degree, number> |
| 16 | +{ |
| 17 | +public: |
| 18 | + using ScalarValue = dealii::VectorizedArray<number>; |
| 19 | + using ScalarGrad = dealii::Tensor<1, dim, ScalarValue>; |
| 20 | + using ScalarHess = dealii::Tensor<2, dim, ScalarValue>; |
| 21 | + using VectorValue = dealii::Tensor<1, dim, ScalarValue>; |
| 22 | + using VectorGrad = dealii::Tensor<2, dim, ScalarValue>; |
| 23 | + using VectorHess = dealii::Tensor<3, dim, ScalarValue>; |
| 24 | + using PDEOperatorBase<dim, degree, number>::get_user_inputs; |
| 25 | + using PDEOperatorBase<dim, degree, number>::get_pf_tools; |
| 26 | + |
| 27 | + /** |
| 28 | + * @brief Constructor. |
| 29 | + */ |
| 30 | + explicit CustomPDE(const UserInputParameters<dim> &_user_inputs, |
| 31 | + PhaseFieldTools<dim> &_pf_tools) |
| 32 | + : PDEOperatorBase<dim, degree, number>(_user_inputs, _pf_tools) |
| 33 | + , mobility(get_user_inputs().user_constants.get_double("mobility")) |
| 34 | + , kappa(get_user_inputs().user_constants.get_double("kappa")) |
| 35 | + {} |
| 36 | + |
| 37 | + void |
| 38 | + set_lambda(number v) |
| 39 | + { |
| 40 | + lambda = v; |
| 41 | + } |
| 42 | + |
| 43 | +private: |
| 44 | + number mobility; |
| 45 | + number kappa; |
| 46 | + number lambda; |
| 47 | + |
| 48 | + void |
| 49 | + set_initial_condition([[maybe_unused]] const unsigned int &index, |
| 50 | + [[maybe_unused]] const unsigned int &component, |
| 51 | + [[maybe_unused]] const dealii::Point<dim> &point, |
| 52 | + [[maybe_unused]] number &scalar_value, |
| 53 | + [[maybe_unused]] number &vector_component_value) const override |
| 54 | + { |
| 55 | + const dealii::Tensor<1, dim> &mesh_size = |
| 56 | + get_user_inputs().spatial_discretization.rectangular_mesh.size; |
| 57 | + constexpr number center[12][3] = { |
| 58 | + {0.1, 0.3, 0}, |
| 59 | + {0.8, 0.7, 0}, |
| 60 | + {0.5, 0.2, 0}, |
| 61 | + {0.4, 0.4, 0}, |
| 62 | + {0.3, 0.9, 0}, |
| 63 | + {0.8, 0.1, 0}, |
| 64 | + {0.9, 0.5, 0}, |
| 65 | + {0.0, 0.1, 0}, |
| 66 | + {0.1, 0.6, 0}, |
| 67 | + {0.5, 0.6, 0}, |
| 68 | + {1, 1, 0}, |
| 69 | + {0.7, 0.95, 0} |
| 70 | + }; |
| 71 | + constexpr number rad[12] = {12, 14, 19, 16, 11, 12, 17, 15, 20, 10, 11, 14}; |
| 72 | + |
| 73 | + number dist = 0.0; |
| 74 | + number sdf = std::numeric_limits<double>::max(); |
| 75 | + for (unsigned int i = 0; i < 12; i++) |
| 76 | + { |
| 77 | + dist = 0.0; |
| 78 | + for (unsigned int dir = 0; dir < dim; dir++) |
| 79 | + { |
| 80 | + const number comp_diff = point[dir] - center[i][dir] * mesh_size[dir]; |
| 81 | + dist += comp_diff * comp_diff; |
| 82 | + } |
| 83 | + dist = std::sqrt(dist) - rad[i]; |
| 84 | + sdf = std::min(sdf, dist); |
| 85 | + } |
| 86 | + scalar_value = 0.5 * (1.0 - std::tanh(sdf / 1.5)); |
| 87 | + } |
| 88 | + |
| 89 | + void |
| 90 | + compute_rhs(FieldContainer<dim, degree, number> &variable_list, |
| 91 | + const SimulationTimer &sim_timer, |
| 92 | + unsigned int solve_block_id) const override |
| 93 | + { |
| 94 | + if (solve_block_id == 0) // explicit c |
| 95 | + { |
| 96 | + ScalarValue c = variable_list.template get_value<Scalar, OldOne>(0); |
| 97 | + ScalarValue mu = variable_list.template get_value<Scalar, OldOne>(1); |
| 98 | + |
| 99 | + ScalarValue eq_c = c - sim_timer.get_timestep() * mobility * (mu - lambda); |
| 100 | + |
| 101 | + variable_list.set_value_term(0, eq_c); |
| 102 | + } |
| 103 | + else if (solve_block_id == 1) // mu |
| 104 | + { |
| 105 | + ScalarValue c = variable_list.template get_value<Scalar, Current>(0); |
| 106 | + ScalarGrad c_grad = variable_list.template get_gradient<Scalar, Current>(0); |
| 107 | + ScalarValue df_well = 4.0 * c * (c - 1.0) * (c - 0.5); |
| 108 | + |
| 109 | + variable_list.set_value_term(1, df_well); |
| 110 | + variable_list.set_gradient_term(1, kappa * c_grad); |
| 111 | + } |
| 112 | + else if (solve_block_id == 2) // custom |
| 113 | + { |
| 114 | + } |
| 115 | + else if (solve_block_id == 3) // postprocess |
| 116 | + { |
| 117 | + ScalarValue c = variable_list.template get_value<Scalar, Current>(0); |
| 118 | + ScalarGrad c_grad = variable_list.template get_gradient<Scalar, Current>(0); |
| 119 | + ScalarValue f_chem = c * c * (1.0 - c) * (1.0 - c); |
| 120 | + ScalarValue f_grad = 0.5 * kappa * c_grad.norm_square(); |
| 121 | + |
| 122 | + variable_list.set_value_term(3, c_grad.norm()); |
| 123 | + variable_list.set_value_term(4, f_chem + f_grad); |
| 124 | + } |
| 125 | + } |
| 126 | +}; |
| 127 | + |
| 128 | +template <unsigned int dim, unsigned int degree, typename number> |
| 129 | +class MyCustomSolver : public SolverBase<dim, degree, number> |
| 130 | +{ |
| 131 | +private: |
| 132 | + CustomPDE<dim, degree, number> &custom_pde; |
| 133 | + |
| 134 | +public: |
| 135 | + MyCustomSolver(const SolveBlock &solve_block, |
| 136 | + const SolveContext<dim, degree, number> &solve_context, |
| 137 | + CustomPDE<dim, degree, number> &_custom_pde) |
| 138 | + : SolverBase<dim, degree, number>(solve_block, solve_context) |
| 139 | + , custom_pde(_custom_pde) |
| 140 | + {} |
| 141 | + |
| 142 | + void |
| 143 | + init(const std::list<DependencyMap> &all_dependency_sets) override |
| 144 | + { |
| 145 | + SolverBase<dim, degree, number>::init(all_dependency_sets); |
| 146 | + } |
| 147 | + |
| 148 | + void |
| 149 | + solve_level(unsigned int relative_level) override |
| 150 | + { |
| 151 | + number mu_int = Integrator<dim, degree, number>::template integrate<0>( |
| 152 | + this->solve_context->get_dof_manager().get_field_dof_handler(1), |
| 153 | + this->solve_context->get_solution_indexer().get_solution_vector(1))[0]; |
| 154 | + |
| 155 | + const dealii::Tensor<1, dim> &mesh_size = |
| 156 | + this->solve_context->get_user_inputs().spatial_discretization.rectangular_mesh.size; |
| 157 | + number V = mesh_size[0] * mesh_size[1]; |
| 158 | + |
| 159 | + custom_pde.set_lambda(mu_int / V); |
| 160 | + } |
| 161 | + |
| 162 | + void |
| 163 | + update() override |
| 164 | + {} |
| 165 | +}; |
| 166 | + |
| 167 | +PRISMS_PF_END_NAMESPACE |
0 commit comments