applications/mp-melt-pool/cases/stefans_problem/stefans_problem1_with_flow_and_heat.hpp Source File

Developer Documentation: applications/mp-melt-pool/cases/stefans_problem/stefans_problem1_with_flow_and_heat.hpp Source File
Developer Documentation
stefans_problem1_with_flow_and_heat.hpp
Go to the documentation of this file.
1#pragma once
2// deal-specific libraries
3#include <deal.II/base/function.h>
4#include <deal.II/base/function_signed_distance.h>
5#include <deal.II/base/mpi.h>
6#include <deal.II/base/mpi.templates.h>
7#include <deal.II/base/point.h>
8#include <deal.II/base/tensor_function.h>
9
10#include <deal.II/grid/grid_generator.h>
11#include <deal.II/grid/grid_tools.h>
12#include <deal.II/grid/tria.h>
13
14#include <deal.II/lac/vector.h>
15
16#include <deal.II/numerics/vector_tools.h>
17
20
21#include <boost/math/tools/roots.hpp>
22
23#include <cmath>
24#include <iostream>
25#include <string>
26
27#include "../../melt_pool_case.hpp"
28
70{
71 using namespace MeltPoolDG::Simulation;
72
73 static constexpr double x_min = 0.0;
74 static constexpr double y_min = 0.0;
75 static constexpr double y_max = 1.e-3;
76
77
78 namespace AnalyticalSolution
79 {
80
81 template <typename number>
82 double
83 compute_beta(const MeltPoolCaseParameters<number> &parameters, const double T_wall)
84 {
85 const auto beta_func = [&](double beta) -> double {
86 return beta * std::exp(beta * beta) * erf(beta) -
87 parameters.material.gas.specific_heat_capacity *
88 (T_wall - parameters.material.boiling_temperature) /
89 (parameters.material.latent_heat_of_evaporation * std::sqrt(dealii::numbers::PI));
90 };
91
92 std::pair<double, double> result =
93 boost::math::tools::bisect(beta_func, 1e-6, 100., [&](double min, double max) -> bool {
94 return std::abs(max - min) <= 1e-10;
95 });
96
97 return (result.first + result.second) / 2;
98 }
99
100 template <typename number>
101 double
103 const double T_wall,
104 const double time,
105 const double beta = -1.0)
106 {
107 if (beta == -1.0)
108 compute_beta(parameters, T_wall);
109
110 return 2. * beta *
111 std::sqrt(
112 parameters.material.gas.thermal_conductivity /
113 (parameters.material.gas.density * parameters.material.gas.specific_heat_capacity) *
114 time);
115 }
116
117 template <typename number>
118 double
120 const double T_wall,
121 const double time,
122 const double x,
123 const double beta = -1.0)
124 {
125 if (beta == -1.0)
126 compute_beta(parameters, T_wall);
127
128 const double diffusivity =
129 parameters.material.gas.thermal_conductivity /
130 (parameters.material.gas.density * parameters.material.gas.specific_heat_capacity);
131
132 if (time == 0)
133 return parameters.material.boiling_temperature;
134 else
135 return std::max(T_wall + (parameters.material.boiling_temperature - T_wall) *
136 erf(x / (2. * std::sqrt(diffusivity * time))) / erf(beta),
137 parameters.material.boiling_temperature);
138 }
139 } // namespace AnalyticalSolution
140
141 template <int dim>
142 class InitialValuesTemperature : public dealii::Function<dim>
143 {
144 public:
145 InitialValuesTemperature(const double T_sat, const double T_wall, const double y_interface)
146 : dealii::Function<dim>()
147 , T_sat(T_sat)
148 , T_wall(T_wall)
150
151 {}
152
153 double
154 value(const dealii::Point<dim> &p, const unsigned int /*component*/) const final
155 {
156 return std::max(T_wall - (T_wall - T_sat) * p[dim - 1] / y_interface, T_sat);
157 }
158
159 const double T_sat, T_wall, y_interface;
160 };
161 /*
162 * This class collects all relevant input data for the level set simulation
163 */
164
165 template <int dim, typename number>
167 {
168 public:
170 const MPI_Comm mpi_communicator)
172 , x_max(y_max / std::pow(dim, this->parameters.base.global_refinements))
174 {
175 AssertThrow(y_interface >= y_min && y_interface <= y_max,
176 dealii::ExcMessage(
177 "The location of the initial interface must be between y_min and y_max."));
178
179 file_name_level_set_contour = this->parameters.output.directory + "/" +
180 this->parameters.output.paraview.filename +
181 "_level_set_contour_over_time.txt";
182 }
183
184 bool
185 add_case_specific_parameters(dealii::ParameterHandler &prm) override
186 {
187 prm.add_parameter("y interface", y_interface, "initial interface location");
188 prm.add_parameter("T wall", T_wall, "heated temperature of the wall");
189
190 return this->parameters.base.do_print_parameters;
191 }
192
193 void
195 {
196 if (this->parameters.base.fe.type == FiniteElementType::FE_SimplexP || dim == 1)
197 {
198#ifdef DEAL_II_WITH_METIS
199 this->triangulation = std::make_shared<dealii::parallel::shared::Triangulation<dim>>(
200 this->mpi_communicator,
201 dealii::Triangulation<dim>::none,
202 false,
203 dealii::parallel::shared::Triangulation<dim>::Settings::partition_metis);
204#else
205 AssertThrow(
206 false,
207 dealii::ExcMessage(
208 "Missing Metis support of the deal.II installation. "
209 "Configure deal.II with -D DEAL_II_WITH_METIS='ON' to execute this example."));
210#endif
211 }
212 else
213 {
214 this->triangulation = std::make_shared<dealii::parallel::distributed::Triangulation<dim>>(
215 this->mpi_communicator);
216 }
217
218 const unsigned int n_elements_per_edge =
219 dim > 1 ? std::pow(dim, this->parameters.base.global_refinements) :
220 this->parameters.base.global_refinements;
221
222 std::vector<unsigned int> refinements(dim, 1);
223 refinements[dim - 1] = n_elements_per_edge;
224
225 // create mesh
226 const dealii::Point<dim> bottom_left = dim == 1 ? dealii::Point<dim>(y_min) :
227 dim == 2 ? dealii::Point<dim>(x_min, y_min) :
228 dealii::Point<dim>(x_min, x_min, y_min);
229 const dealii::Point<dim> top_right = dim == 1 ? dealii::Point<dim>(y_max) :
230 dim == 2 ? dealii::Point<dim>(x_max, y_max) :
231 dealii::Point<dim>(x_max, x_max, y_max);
232
233 if (this->parameters.base.fe.type == FiniteElementType::FE_SimplexP)
234 {
235 // create mesh
236 std::vector<unsigned int> subdivisions(
237 dim, 5 * dealii::Utilities::pow(2, this->parameters.base.global_refinements));
238 subdivisions[dim - 1] *= 2;
239
240 dealii::GridGenerator::subdivided_hyper_rectangle_with_simplices(
241 *this->triangulation, subdivisions, bottom_left, top_right, true /*colorize*/);
242 }
243 else
244 {
245 dealii::GridGenerator::subdivided_hyper_rectangle(
246 *this->triangulation, refinements, bottom_left, top_right, true /*colorize*/);
247 }
248
249
250 // get vertices along the vertical axis on rank 0
251 if (dealii::Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0)
252 {
253 const unsigned int n_elements = std::min<double>(200, n_elements_per_edge);
254 for (unsigned int i = 0; i <= n_elements; ++i)
255 {
256 auto p = dealii::Point<dim>();
257 p[dim - 1] = y_min + (y_max - y_min) / n_elements * i;
258 vertices_along_vertical_axis.emplace_back(p);
259 }
260 }
261 }
262
263 void
265 {
266 // faces in dim-1 direction
267 const dealii::types::boundary_id bottom_bc = 2 * (dim - 1);
268 const dealii::types::boundary_id top_bc = bottom_bc + 1;
269
270 // lower part = gas; upper part = liquid
272 {bottom_bc, std::make_shared<dealii::Functions::ConstantFunction<dim>>(-1.0)},
273 "dirichlet",
274 "level_set");
276 {bottom_bc, std::make_shared<dealii::Functions::ConstantFunction<dim>>(T_wall)},
277 "dirichlet",
278 "heat_transfer");
279 this->attach_boundary_condition({top_bc,
280 std::make_shared<dealii::Functions::ConstantFunction<dim>>(
281 this->parameters.material.boiling_temperature)},
282 "dirichlet",
283 "heat_transfer");
284
285 // dummy BC for Navier-Stokes
286 this->attach_boundary_condition(bottom_bc, "no_slip", "navier_stokes_u");
287 this->attach_boundary_condition(top_bc, "fix_pressure_constant", "navier_stokes_p");
288
289 // collect boundary ids of side walls
290 std::vector<dealii::types::boundary_id> side_walls;
291
292 if (dim > 1)
293 for (unsigned int i = 0; i < 2 * (dim - 1); ++i)
294 side_walls.push_back(i);
295
296 // set PBC on side walls
297 if (dim >= 2)
298 this->attach_periodic_boundary_condition(side_walls[0], side_walls[1], 0);
299 if (dim == 3)
300 this->attach_periodic_boundary_condition(side_walls[2], side_walls[3], 1);
301 }
302
303 void
305 {
306 this->attach_initial_condition(std::make_shared<dealii::Functions::ZeroFunction<dim>>(dim),
307 "navier_stokes_u");
309 std::make_shared<dealii::Functions::SignedDistance::Plane<dim>>(
310 dealii::Point<dim>::unit_vector(dim - 1) * y_interface,
311 dealii::Point<dim>::unit_vector(dim - 1)),
312 "signed_distance");
313
315 this->parameters.material.boiling_temperature,
316 T_wall,
318 "heat_transfer");
319 }
320
321 void
322 do_postprocessing(const GenericDataOut<dim, double> &generic_data_out) const final
323 {
324 // first time postprocessing
325 if (beta == -1.0)
326 {
328 if (dealii::Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0)
329 {
332 << "time interface_location interface_temperature analytical_interface_location(beta=" +
333 std::to_string(beta) + ")"
334 << std::endl;
336 }
337 }
338
339 if (((this->parameters.output.paraview.enable) &&
340 !(n_time_step % this->parameters.output.write_frequency)) ||
341 generic_data_out.get_time() == this->parameters.time_stepping.end_time)
342 {
343 generic_data_out.get_vector("level_set").update_ghost_values();
344 generic_data_out.get_vector("temperature").update_ghost_values();
345
346 /*
347 * evaluate temperature profile
348 */
350 {
352 *this->triangulation,
353 generic_data_out.get_mapping());
355 }
356
357 const auto temperature_evaluation_values =
358 dealii::VectorTools::point_values<1>(remote_point_evaluation,
359 generic_data_out.get_dof_handler("temperature"),
360 generic_data_out.get_vector("temperature"));
361
362 // write values to file
363 if (dealii::Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0)
364 {
365 const auto file_name = this->parameters.output.directory + "/" +
366 this->parameters.output.paraview.filename +
367 "_temperature_profile_" +
368 std::to_string(generic_data_out.get_time()) + ".txt";
369 file_temperature_profile.open(file_name);
371 << "coordinate temperature analytical_temperature(beta=" + std::to_string(beta) +
372 ")"
373 << std::endl;
374
375 for (unsigned int i = 0; i < temperature_evaluation_values.size(); ++i)
376 {
378 << temperature_evaluation_values[i] << " "
380 this->parameters,
381 T_wall,
382 generic_data_out.get_time(),
384 beta)
385 << std::endl;
386 }
388 }
389
390 /*
391 * evaluate location of and temperature at level set == 0
392 */
393 dealii::FEPointEvaluation<1, dim> temperature_eval(
394 generic_data_out.get_mapping(),
395 generic_data_out.get_dof_handler("temperature").get_fe(),
396 dealii::update_values);
397
398 std::vector<std::pair<dealii::Point<dim>, double>> vertices_and_temperatures;
399
400 std::vector<double> buffer;
401 std::vector<dealii::types::global_dof_index> local_dof_indices;
402
403 LevelSet::Tools::evaluate_at_interface<dim, double>(
404 generic_data_out.get_dof_handler("level_set"),
405 generic_data_out.get_mapping(),
406 generic_data_out.get_vector("level_set"),
407 [&](const auto &cell,
408 const auto &points_real,
409 const auto &points,
410 [[maybe_unused]] const auto &weights) {
411 local_dof_indices.resize(cell->get_fe().n_dofs_per_cell());
412 buffer.resize(cell->get_fe().n_dofs_per_cell());
413 cell->get_dof_indices(local_dof_indices);
414
415 const unsigned int n_points = points.size();
416
417 const dealii::ArrayView<const dealii::Point<dim>> unit_points(points.data(),
418 n_points);
419 temperature_eval.reinit(cell, unit_points);
420
421 cell->get_dof_values(generic_data_out.get_vector("temperature"),
422 buffer.begin(),
423 buffer.end());
424
425 // evaluate temperature and level set points
426 temperature_eval.evaluate(buffer, dealii::EvaluationFlags::values);
427 for (unsigned int q = 0; q < n_points; ++q)
428 {
429 vertices_and_temperatures.emplace_back(points_real[q],
430 temperature_eval.get_value(q));
431 }
432 },
433 0.0, /*contour value*/
434 3 /*n_subdivisions*/);
435
436 // collect result on rank 0 to write them to file
437 const auto vertices_and_temperatures_all =
438 dealii::Utilities::MPI::reduce<std::vector<std::pair<dealii::Point<dim>, double>>>(
439 vertices_and_temperatures, this->mpi_communicator, [](const auto &a, const auto &b) {
440 auto result = a;
441 result.insert(result.end(), b.begin(), b.end());
442 return result;
443 });
444
445 // compute analytical solution
446 const double interface_analytical = AnalyticalSolution::analytical_interface_location(
447 this->parameters, T_wall, generic_data_out.get_time(), beta);
448 // write values to file
449 if (dealii::Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0)
450 {
453 << generic_data_out.get_time() << " "
454 << vertices_and_temperatures_all[0].first[dim - 1] - y_interface << " "
455 << vertices_and_temperatures_all[0].second << " " << interface_analytical
456 << std::endl;
458 }
459
460 dealii::ConditionalOStream pcout(
461 std::cout, dealii::Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0);
462 pcout << "interface_numerical " << vertices_and_temperatures_all[0].first[dim - 1]
463 << " interface_analytical " << interface_analytical << " absolute error: "
464 << std::abs(interface_analytical - vertices_and_temperatures_all[0].first[dim - 1])
465 << std::endl;
466 generic_data_out.get_vector("level_set").zero_out_ghost_values();
467 generic_data_out.get_vector("temperature").zero_out_ghost_values();
468 }
469 n_time_step += 1;
470 }
471
472
473 private:
474 const double x_max;
475 double y_interface = 5.e-5;
476 double T_wall = 383.15;
477
478 // post-processing
479 std::vector<dealii::Point<dim>> vertices_along_vertical_axis;
480 mutable std::ofstream file_level_set_contour;
482 mutable std::ofstream file_temperature_profile;
483 mutable int n_time_step = 0.0;
484 mutable double beta = -1.0;
485 mutable dealii::Utilities::MPI::RemotePointEvaluation<dim, dim> remote_point_evaluation;
486 mutable bool remote_point_is_initialized = false;
487 };
488} // namespace MeltPoolDG::Simulation::StefansProblem1WithFlowAndHeat
A generic utility for managing simulation output data in the MeltPoolDG context.
Definition generic_data_out.hpp:32
Definition melt_pool_case.hpp:290
MeltPoolCaseParameters< number > parameters
Definition melt_pool_case.hpp:292
void attach_boundary_condition(std::pair< const dealii::types::boundary_id, const std::shared_ptr< dealii::Function< dim > > > id_and_function, const std::string &type, const std::string &operation_name)
Attach a boundary condition for a specific operation.
Definition simulation_case_base.hpp:345
std::shared_ptr< dealii::Triangulation< dim, spacedim > > triangulation
Definition simulation_case_base.hpp:49
const std::string parameter_file
Definition simulation_case_base.hpp:52
void attach_periodic_boundary_condition(const dealii::types::boundary_id id_in, const dealii::types::boundary_id id_out, const int direction)
Definition simulation_case_base.hpp:384
void attach_initial_condition(std::shared_ptr< dealii::Function< dim > > initial_function, const std::string &operation_name)
Definition simulation_case_base.hpp:327
const MPI_Comm mpi_communicator
Definition simulation_case_base.hpp:56
Definition stefans_problem1_with_flow_and_heat.hpp:143
double value(const dealii::Point< dim > &p, const unsigned int) const final
Definition stefans_problem1_with_flow_and_heat.hpp:154
const double T_sat
Definition stefans_problem1_with_flow_and_heat.hpp:159
const double T_wall
Definition stefans_problem1_with_flow_and_heat.hpp:159
const double y_interface
Definition stefans_problem1_with_flow_and_heat.hpp:159
InitialValuesTemperature(const double T_sat, const double T_wall, const double y_interface)
Definition stefans_problem1_with_flow_and_heat.hpp:145
void create_spatial_discretization() override
Pure virtual function to create the spatial discretization.
Definition stefans_problem1_with_flow_and_heat.hpp:194
SimulationStefansProblem1WithFlowAndHeat(std::string parameter_file, const MPI_Comm mpi_communicator)
Definition stefans_problem1_with_flow_and_heat.hpp:169
void do_postprocessing(const GenericDataOut< dim, double > &generic_data_out) const final
Definition stefans_problem1_with_flow_and_heat.hpp:322
std::string file_name_level_set_contour
Definition stefans_problem1_with_flow_and_heat.hpp:481
std::vector< dealii::Point< dim > > vertices_along_vertical_axis
Definition stefans_problem1_with_flow_and_heat.hpp:479
dealii::Utilities::MPI::RemotePointEvaluation< dim, dim > remote_point_evaluation
Definition stefans_problem1_with_flow_and_heat.hpp:485
void set_boundary_conditions() final
Pure virtual function to set the boundary conditions.
Definition stefans_problem1_with_flow_and_heat.hpp:264
std::ofstream file_level_set_contour
Definition stefans_problem1_with_flow_and_heat.hpp:480
std::ofstream file_temperature_profile
Definition stefans_problem1_with_flow_and_heat.hpp:482
const double x_max
Definition stefans_problem1_with_flow_and_heat.hpp:474
bool remote_point_is_initialized
Definition stefans_problem1_with_flow_and_heat.hpp:486
void set_field_conditions() final
Pure virtual function to set the field conditions.
Definition stefans_problem1_with_flow_and_heat.hpp:304
bool add_case_specific_parameters(dealii::ParameterHandler &prm) override
Add simulation-specific parameters (can be overridden).
Definition stefans_problem1_with_flow_and_heat.hpp:185
double compute_beta(const MeltPoolCaseParameters< number > &parameters, const double T_wall)
Definition stefans_problem1_with_flow_and_heat.hpp:83
double analytical_temperature(const MeltPoolCaseParameters< number > &parameters, const double T_wall, const double time, const double x, const double beta=-1.0)
Definition stefans_problem1_with_flow_and_heat.hpp:119
double analytical_interface_location(const MeltPoolCaseParameters< number > &parameters, const double T_wall, const double time, const double beta=-1.0)
Definition stefans_problem1_with_flow_and_heat.hpp:102
Definition stefans_problem1_with_flow_and_heat.cpp:6
static constexpr double y_min
Definition stefans_problem1_with_flow_and_heat.hpp:74
static constexpr double x_min
Definition stefans_problem1_with_flow_and_heat.hpp:73
static constexpr double y_max
Definition stefans_problem1_with_flow_and_heat.hpp:75
Definition advection_diffusion.cpp:6
Definition dealii_tensor.hpp:10
Definition melt_pool_case.hpp:52
MaterialData< number > material
Definition melt_pool_case.hpp:235