applications/mp-level-set/cases/vortex_bubble.hpp Source File

Developer Documentation: applications/mp-level-set/cases/vortex_bubble.hpp Source File
Developer Documentation
vortex_bubble.hpp
Go to the documentation of this file.
1#pragma once
2#include <deal.II/base/function.h>
3#include <deal.II/base/function_signed_distance.h>
4#include <deal.II/base/point.h>
5#include <deal.II/base/table_handler.h>
6#include <deal.II/base/tensor_function.h>
7
8#include <deal.II/distributed/tria.h>
9
10#include <deal.II/grid/grid_generator.h>
11
12#include <deal.II/lac/vector.h>
13
14#include <deal.II/numerics/error_estimator.h>
15#include <deal.II/numerics/vector_tools.h>
16
21
22#include <cmath>
23#include <filesystem>
24#include <fstream>
25#include <iostream>
26
27#include "../level_set_case.hpp"
28
30{
31 // period of the vortex flow
32 static double Tf = 2.0;
33
34 /*
35 * this function specifies the initial field of the level set equation
36 */
37 template <int dim, typename number>
38 class InitializePhi : public dealii::Function<dim, number>
39 {
40 public:
42 : dealii::Function<dim, number>()
43 , distance_sphere(dim == 1 ? dealii::Point<dim, number>(0.5) :
44 dealii::Point<dim, number>(0.5, 0.75),
45 0.15)
46 {}
47
48 number
49 value(const dealii::Point<dim, number> &p, const unsigned int /*component*/) const override
50 {
51 return -distance_sphere.value(p);
52 }
53
54 private:
55 const dealii::Functions::SignedDistance::Sphere<dim> distance_sphere;
56 };
57
58 template <int dim, typename number>
59 class PhiExact : public dealii::Function<dim, number>
60 {
61 public:
62 PhiExact(const number eps)
63 : eps(eps)
64 {}
65
66 number
67 value(const dealii::Point<dim, number> &p, const unsigned int /*component*/) const override
68 {
69 return std::tanh(distance_sphere.value(p, 0) / (2 * eps));
70 }
71
72 private:
73 const number eps;
75 };
76
77 template <int dim, typename number>
78 class SignedDistanceExact : public dealii::Function<dim, number>
79 {
80 public:
82 : eps(eps)
83 {}
84
85 number
86 value(const dealii::Point<dim, number> &p, const unsigned int /*component*/) const override
87 {
88 const auto val = distance_sphere.value(p, 0);
89 return val > 0 ? std::min(val, 8 * eps) : std::max(val, -8 * eps);
90 }
91
92 private:
93 const number eps;
95 };
96
97 template <int dim, typename number>
98 class AdvectionField : public dealii::Function<dim, number>
99 {
100 public:
102 : dealii::Function<dim, number>(dim)
103 {}
104
105 void
106 vector_value(const dealii::Point<dim, number> &p, dealii::Vector<number> &values) const override
107 {
108 if constexpr (dim == 2)
109 {
110 const number time = this->get_time();
111
112 const number x = p[0];
113 const number y = p[1];
114
115 const number reverseCoefficient = std::cos(dealii::numbers::PI * time / Tf);
116
117 values[0] = reverseCoefficient * (std::sin(2. * dealii::numbers::PI * y) *
118 std::pow(std::sin(dealii::numbers::PI * x), 2.));
119 values[1] = reverseCoefficient * (-std::sin(2. * dealii::numbers::PI * x) *
120 std::pow(std::sin(dealii::numbers::PI * y), 2.));
121 }
122 else
123 AssertThrow(false, dealii::ExcMessage("Advection field for dim!=2 not implemented"));
124 }
125 };
126
127 /* for constant Dirichlet conditions we could also use the ConstantFunction
128 * utility from dealii
129 */
130 template <int dim, typename number>
131 class DirichletCondition : public dealii::Function<dim, number>
132 {
133 public:
135 : dealii::Function<dim, number>()
136 {}
137
138 number
139 value(const dealii::Point<dim, number> &p, const unsigned int component = 0) const override
140 {
141 (void)p;
142 (void)component;
143 return -1.0;
144 }
145 };
146
147 /*
148 * This class collects all relevant input data for the level set simulation
149 */
150
151 template <int dim, typename number>
153 {
154 public:
156 : LevelSet::LevelSetCase<dim, number>(parameter_file, mpi_communicator)
157 {}
158
159 void
161 {
162 if (dim == 1 || this->parameters.base.fe.type == FiniteElementType::FE_SimplexP)
163 {
164 AssertDimension(dealii::Utilities::MPI::n_mpi_processes(this->mpi_communicator), 1);
165 this->triangulation = std::make_shared<dealii::Triangulation<dim>>();
166 }
167 else
168 {
169 this->triangulation = std::make_shared<dealii::parallel::distributed::Triangulation<dim>>(
170 this->mpi_communicator);
171 }
172
173 if (this->parameters.base.fe.type == FiniteElementType::FE_SimplexP)
174 {
175 dealii::GridGenerator::subdivided_hyper_cube_with_simplices(
176 *this->triangulation,
177 dealii::Utilities::pow(2, this->parameters.base.global_refinements),
180 }
181 else
182 {
183 dealii::GridGenerator::hyper_cube(*this->triangulation, left_domain, right_domain);
184 this->triangulation->refine_global(this->parameters.base.global_refinements);
185 }
186 }
187
188 void
190 {
191 // none
192 }
193
194 void
196 {
198 "signed_distance");
199 this->attach_field_function(std::make_shared<AdvectionField<dim, number>>(),
200 "prescribed_velocity",
201 "level_set");
202 }
203
204 void
205 do_postprocessing(const GenericDataOut<dim, number> &generic_data_out) const final
206 {
207 if constexpr (dim == 2)
208 {
209 dealii::ConditionalOStream pcout(
210 std::cout, dealii::Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0);
211 // compute area
212 const auto n_q_points = this->parameters.base.fe.degree + 3;
213 dealii::FE_Q<dim> fe(this->parameters.base.fe.degree);
214
215 dealii::QGauss<dim> quadrature(n_q_points);
216 dealii::FEValues<dim> fe_values(fe,
217 quadrature,
218 dealii::update_values | dealii::update_JxW_values |
219 dealii::update_quadrature_points);
220
221 std::vector<number> phi_at_q(dealii::QGauss<dim>(n_q_points).size());
222
223 std::vector<number> volume_fraction;
224 number area_droplet = 0;
225 number area_bulk = 0;
226 const number threshhold = 0.0;
227
228 generic_data_out.get_vector("level_set").update_ghost_values();
229
230 for (const auto &cell :
231 generic_data_out.get_dof_handler("level_set").active_cell_iterators())
232 if (cell->is_locally_owned())
233 {
234 fe_values.reinit(cell);
235 fe_values.get_function_values(generic_data_out.get_vector("level_set"),
236 phi_at_q); // compute values of old solution
237
238 for (const unsigned int q_index : fe_values.quadrature_point_indices())
239 {
240 if (phi_at_q[q_index] >= threshhold)
241 area_droplet += fe_values.JxW(q_index);
242 else
243 area_bulk += fe_values.JxW(q_index);
244 }
245 }
246 generic_data_out.get_vector("level_set").zero_out_ghost_values();
247
248 area_droplet = dealii::Utilities::MPI::sum(area_droplet, this->mpi_communicator);
249 area_bulk = dealii::Utilities::MPI::sum(area_bulk, this->mpi_communicator);
250
251 pcout << "area (phi>0) " << area_droplet << std::endl;
252 pcout << "area (phi<0) " << area_bulk << std::endl;
253
254 // compute circularity
255 // 1) compute the surface of the droplet
256 dealii::Triangulation<std::max(1, dim - 1), dim> tria_droplet_surface;
257
258 GridGenerator::create_triangulation_with_marching_cube_algorithm<dim>(
259 tria_droplet_surface,
260 dealii::MappingQ<dim>(3),
261 generic_data_out.get_dof_handler("level_set"),
262 generic_data_out.get_vector("level_set"),
263 0. /*iso level*/,
264 1 /*n subdivisions of surface mesh*/);
265
266 number area_droplet_boundary = 0;
267
268 // check if partitioned domains contain surface elements in case of parallel execution
269 if (tria_droplet_surface.n_cells() > 0)
270 area_droplet_boundary =
271 dealii::GridTools::volume<std::max(1, dim - 1), dim>(tria_droplet_surface);
272
273 area_droplet_boundary =
274 dealii::Utilities::MPI::sum(area_droplet_boundary, MPI_COMM_WORLD);
275
276 AssertThrow(area_droplet_boundary >= 1e-16,
277 dealii::ExcMessage("Area of droplet is zero."));
278
279 // 2) compute circularity
280 number circularity =
281 2. * std::sqrt(dealii::numbers::PI * area_droplet) / (area_droplet_boundary);
282 pcout << "circularity " << circularity << std::endl;
283
284 table.add_value("time", generic_data_out.get_time());
285 table.add_value("circularity", circularity);
286 table.add_value("area_droplet", area_droplet);
287
288 if (dealii::Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0)
289 {
290 namespace fs = std::filesystem;
291 std::ofstream output(fs::path(this->parameters.output.directory) /
292 fs::path(this->parameters.output.paraview.filename + ".txt"));
293 table.write_text(output);
294 }
295
296 if (std::abs(generic_data_out.get_time() - this->parameters.time_stepping.end_time) <
297 1e-10 ||
298 (std::abs(generic_data_out.get_time() - this->parameters.time_stepping.start_time) <
299 1e-10))
300 {
301 namespace fs = std::filesystem;
302 std::ofstream output(
303 fs::path(this->parameters.output.directory) /
304 fs::path(this->parameters.output.paraview.filename + "_L2norm.txt"));
305
306
307 generic_data_out.get_vector("distance").update_ghost_values();
308 generic_data_out.get_vector("level_set").update_ghost_values();
309
310 const number eps =
311 this->parameters.ls.reinit.compute_interface_thickness_parameter_epsilon(
312 dealii::GridTools::minimal_cell_diameter(*this->triangulation) /
313 this->parameters.ls.get_n_subdivisions() / std::sqrt(dim));
314
315 // compute L2Norm of level_set
316 dealii::Vector<float> difference_per_cell(this->triangulation->n_active_cells());
317 dealii::VectorTools::integrate_difference(generic_data_out.get_mapping(),
318 generic_data_out.get_dof_handler(
319 "level_set"),
320 generic_data_out.get_vector("level_set"),
322 difference_per_cell,
323 quadrature,
324 dealii::VectorTools::L2_norm);
325
326 const number norm =
327 dealii::VectorTools::compute_global_error(*this->triangulation,
328 difference_per_cell,
329 dealii::VectorTools::L2_norm);
330 difference_per_cell = 0;
331
332 // compute L2Norm of distance
333 dealii::VectorTools::integrate_difference(generic_data_out.get_mapping(),
334 generic_data_out.get_dof_handler(
335 "level_set"),
336 generic_data_out.get_vector("distance"),
338 difference_per_cell,
339 quadrature,
340 dealii::VectorTools::L2_norm);
341 const number norm_sd =
342 dealii::VectorTools::compute_global_error(*this->triangulation,
343 difference_per_cell,
344 dealii::VectorTools::L2_norm);
345
346 // compute relative L2 norm in a finite interval around the interface
348 dealii::FEValues<dim> fe(generic_data_out.get_mapping(),
349 generic_data_out.get_dof_handler("level_set").get_fe(),
350 quadrature,
351 dealii::update_values | dealii::update_JxW_values |
352 dealii::update_quadrature_points);
353
354 const unsigned int n_q_points = fe.get_quadrature().size();
355 std::vector<number> phi_at_q(n_q_points);
356
357 number norm_interface_region = 0;
358 number norm_interface_region_exact = 0;
359 number norm_interface_kreiss = 0;
360
361 for (const auto &cell :
362 generic_data_out.get_dof_handler("level_set").active_cell_iterators())
363 {
364 if (cell->is_locally_owned())
365 {
366 fe.reinit(cell);
367 fe.get_function_values(generic_data_out.get_vector("distance"), phi_at_q);
368
369 for (const auto &q : fe.quadrature_point_indices())
370 {
371 norm_interface_kreiss +=
372 dealii::Utilities::fixed_power<2>(
375 sd_circle.value(fe.quadrature_point(q), 0))) *
376 fe.JxW(q);
377 if (std::abs(phi_at_q[q]) < 0.02)
378 {
379 norm_interface_region +=
380 dealii::Utilities::fixed_power<2>(
381 sd_circle.value(fe.quadrature_point(q), 0) - phi_at_q[q]) *
382 fe.JxW(q);
383 norm_interface_region_exact +=
384 dealii::Utilities::fixed_power<2>(
385 sd_circle.value(fe.quadrature_point(q), 0)) *
386 fe.JxW(q);
387 }
388 }
389 }
390 }
391
392 norm_interface_region_exact =
393 std::sqrt(dealii::Utilities::MPI::sum(norm_interface_region_exact, MPI_COMM_WORLD));
394 norm_interface_region =
395 std::sqrt(dealii::Utilities::MPI::sum(norm_interface_region, MPI_COMM_WORLD));
396 norm_interface_kreiss =
397 std::sqrt(dealii::Utilities::MPI::sum(norm_interface_kreiss, MPI_COMM_WORLD));
398
399 generic_data_out.get_vector("level_set").zero_out_ghost_values();
400 generic_data_out.get_vector("distance").zero_out_ghost_values();
401
402 if (dealii::Utilities::MPI::this_mpi_process(this->mpi_communicator) == 0)
403 output
404 << "n_dofs L2_norm(phi-phiExact) L2_norm(signed_distance-exact) relL2_normGamma(signed_distance-exact) L2_normGamma(sd_exact) L2(Kreiss)"
405 << std::endl
406 << generic_data_out.get_dof_handler("level_set").n_dofs() << " " << norm << " "
407 << norm_sd << " " << norm_interface_region / norm_interface_region_exact << " "
408 << norm_interface_region_exact << " " << norm_interface_kreiss;
409 }
410 }
411 }
412
413 bool
414 add_case_specific_parameters(dealii::ParameterHandler &prm) override
415 {
416 prm.add_parameter("T", Tf, "Period of vortex flow.");
417
418 return this->parameters.base.do_print_parameters;
419 }
420
421 private:
422 number left_domain = 0.0;
423 number right_domain = 1.0;
424 mutable dealii::TableHandler table;
425 };
426} // namespace MeltPoolDG::Simulation::VortexBubble
A generic utility for managing simulation output data in the MeltPoolDG context.
Definition generic_data_out.hpp:32
Definition level_set_case.hpp:76
LevelSetCase(const std::string &parameter_file_in, MPI_Comm mpi_communicator_in)
Definition level_set_case.hpp:80
LevelSetCaseParameters< number > parameters
Definition level_set_case.hpp:78
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_initial_condition(std::shared_ptr< dealii::Function< dim > > initial_function, const std::string &operation_name)
Definition simulation_case_base.hpp:327
void attach_field_function(std::shared_ptr< dealii::Function< dim > > function, const std::string &type, const std::string &operation_name)
Definition simulation_case_base.hpp:315
const MPI_Comm mpi_communicator
Definition simulation_case_base.hpp:56
AdvectionField()
Definition vortex_bubble.hpp:101
void vector_value(const dealii::Point< dim, number > &p, dealii::Vector< number > &values) const override
Definition vortex_bubble.hpp:106
number value(const dealii::Point< dim, number > &p, const unsigned int component=0) const override
Definition vortex_bubble.hpp:139
DirichletCondition()
Definition vortex_bubble.hpp:134
const dealii::Functions::SignedDistance::Sphere< dim > distance_sphere
Definition vortex_bubble.hpp:55
InitializePhi()
Definition vortex_bubble.hpp:41
number value(const dealii::Point< dim, number > &p, const unsigned int) const override
Definition vortex_bubble.hpp:49
Definition vortex_bubble.hpp:60
const InitializePhi< dim, number > distance_sphere
Definition vortex_bubble.hpp:74
PhiExact(const number eps)
Definition vortex_bubble.hpp:62
number value(const dealii::Point< dim, number > &p, const unsigned int) const override
Definition vortex_bubble.hpp:67
const number eps
Definition vortex_bubble.hpp:73
const number eps
Definition vortex_bubble.hpp:93
number value(const dealii::Point< dim, number > &p, const unsigned int) const override
Definition vortex_bubble.hpp:86
const InitializePhi< dim, number > distance_sphere
Definition vortex_bubble.hpp:94
SignedDistanceExact(const number eps)
Definition vortex_bubble.hpp:81
void set_boundary_conditions() override
Pure virtual function to set the boundary conditions.
Definition vortex_bubble.hpp:189
SimulationVortexBubble(std::string parameter_file, const MPI_Comm mpi_communicator)
Definition vortex_bubble.hpp:155
bool add_case_specific_parameters(dealii::ParameterHandler &prm) override
Add simulation-specific parameters (can be overridden).
Definition vortex_bubble.hpp:414
void do_postprocessing(const GenericDataOut< dim, number > &generic_data_out) const final
Perform specific postprocessing (can be overridden by derived classes).
Definition vortex_bubble.hpp:205
void create_spatial_discretization() override
Pure virtual function to create the spatial discretization.
Definition vortex_bubble.hpp:160
dealii::TableHandler table
Definition vortex_bubble.hpp:424
void set_field_conditions() override
Pure virtual function to set the field conditions.
Definition vortex_bubble.hpp:195
number left_domain
Definition vortex_bubble.hpp:422
number right_domain
Definition vortex_bubble.hpp:423
number heaviside(const number in, const number limit=0.0)
Definition characteristic_functions.hpp:37
Definition vortex_bubble.cpp:6
static double Tf
Definition vortex_bubble.hpp:32
Definition dealii_tensor.hpp:10