include/meltpooldg/particles/particle_tools.hpp Source File

Developer Documentation: include/meltpooldg/particles/particle_tools.hpp Source File
Developer Documentation
particle_tools.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <deal.II/base/mpi.h>
4#include <deal.II/base/point.h>
5#include <deal.II/base/tensor.h>
6#include <deal.II/base/types.h>
7
11
12#include <cmath>
13#include <fstream>
14#include <optional>
15#include <string>
16#include <utility>
17#include <vector>
18
19namespace MeltPoolDG
20{
29 template <int dim, typename number>
30 inline number
32 {
33 if constexpr (dim == 3)
34 {
35 return 4.0 / 3.0 * M_PI * dealii::Utilities::fixed_power<3, number>(radius);
36 }
37 else if constexpr (dim == 2)
38 {
39 return M_PI * dealii::Utilities::fixed_power<2, number>(radius);
40 }
41 else
42 {
43 AssertThrow(false, dealii::ExcMessage("Invalid dimension!"));
44 }
45 }
46
57 template <int dim, typename number>
58 inline number
59 compute_spherical_particle_moment_of_inertia(const number mass, const number radius)
60 {
61 if constexpr (dim == 3)
62 {
63 return 0.4 * mass * dealii::Utilities::fixed_power<2, number>(radius);
64 }
65 else if constexpr (dim == 2)
66 {
67 return 0.5 * mass * dealii::Utilities::fixed_power<2, number>(radius);
68 }
69 else
70 {
71 AssertThrow(false, dealii::ExcMessage("Invalid dimension!"));
72 }
73 }
74
85 template <int dim, typename number>
86 void
88 const number radius,
89 const number density)
90 {
94 compute_spherical_particle_volume<dim, number>(radius);
99 compute_spherical_particle_moment_of_inertia<dim, number>(
102 }
103
104 template <int dim, typename number, typename VectorizedArrayType>
105 dealii::Tensor<1, dim, VectorizedArrayType>
107 const dealii::Point<dim, VectorizedArrayType> &location)
108 {
109 dealii::Point<dim, VectorizedArrayType> particle_center;
110
111 for (int i = 0; i < dim; ++i)
112 particle_center[i] = VectorizedArrayType(particle.get_location()[i]);
113
114 return particle_center - location;
115 }
116
117 template <int dim, typename number, typename VectorizedArrayType>
118 dealii::Tensor<1, dim, VectorizedArrayType>
120 const dealii::Point<dim, VectorizedArrayType> &location)
121 {
122 Assert(dim == 2 or dim == 3, dealii::ExcMessage("Invalid dimension!"));
123
124 dealii::Point<dim, VectorizedArrayType> vectorized_particle_location;
125 for (auto i = 0; i < dim; ++i)
126 vectorized_particle_location[i] = VectorizedArrayType(particle.get_location()[i]);
127
128 dealii::Tensor<1, dim, VectorizedArrayType> distance_to_center =
129 location - vectorized_particle_location;
130
131 dealii::Tensor<1, dim, VectorizedArrayType> velocity;
132 for (unsigned int i = 0; i < dim; ++i)
133 velocity[i] = VectorizedArrayType(particle.linear_velocity(i));
134
135 dealii::Tensor<1, dim, VectorizedArrayType> angular_velocity;
136 for (unsigned int i = 0; i < axial_dim<dim>; ++i)
137 angular_velocity[i] = VectorizedArrayType(particle.angular_velocity(i));
138
139 if constexpr (dim == 2)
140 {
141 return dealii::cross_product_2d(angular_velocity[0] * distance_to_center) + velocity;
142 }
143 if constexpr (dim == 3)
144 {
145 return velocity + dealii::cross_product_3d(angular_velocity, distance_to_center);
146 }
147 AssertThrow(false, dealii::ExcInternalError());
148 }
149
166 template <int dim, typename number>
167 std::pair<std::vector<dealii::Point<dim, number>>, std::vector<std::vector<number>>>
168 read_particle_state_input_file(const std::string &filename, const MPI_Comm &mpi_communicator)
169 {
170 enum PropertyCSVColumns
171 {
172 position = 0,
173 density = dim,
174 radius
175 };
176
177 // Lambda function to read and parse a single line of the input file. The function reads the
178 // line, computes all particle properties based on the parsed values, and returns the particle
179 // properties and location as a pair. If the line is empty or contains only whitespace, it
180 // returns an empty optional to indicate that no particle should be initialized from this line.
181 // The function throws an exception if the line does not contain the expected number of columns
182 // or if any of the values cannot be converted to a number. Note that no deal.II assertions are
183 // used in this function but only standard exceptions allowing the caller to easily catch and
184 // handle errors in the input file without aborting the entire program.
185 const auto read_values_from_line = [](const std::string &line)
186 -> std::optional<std::pair<std::vector<number>, dealii::Point<dim, number>>> {
187 constexpr unsigned int n_expected_columns = dim + 2; // position (dim) + density + radius
188
189 std::vector<std::string> parsed_strings;
190 parsed_strings.reserve(n_expected_columns);
191
192 std::istringstream data_string(line);
193
194 std::string token;
195 while (std::getline(data_string, token, ','))
196 {
197 parsed_strings.emplace_back(std::move(token));
198 }
199
200 // If the line does not contain any data, return an empty optional to indicate that no
201 // particle should be initialized from this line. This allows for empty lines in the input
202 // file without causing errors.
203 if (parsed_strings.size() == 0 or
204 (parsed_strings.size() == 1 and (parsed_strings[0] == "\r" or parsed_strings[0] == "\n" or
205 parsed_strings[0] == "\t" or parsed_strings[0].empty())))
206 {
207 return std::nullopt;
208 }
209
210 // Check if the expected number of columns is present
211 AssertThrow(parsed_strings.size() == n_expected_columns,
212 ExcInvalidCSVInputColumns(n_expected_columns, parsed_strings.size()));
213
214 // Convert parsed strings to numbers
215 std::vector<number> parsed_values;
216 parsed_values.reserve(n_expected_columns);
217 for (std::string &str : parsed_strings)
218 {
219 // remove trailing whitespace
220 if (!str.empty())
221 {
222 char last_char = str.back();
223
224 if (last_char == '\n' or last_char == '\r' or last_char == '\t')
225 {
226 str.pop_back();
227 }
228 }
229
230 std::size_t characters_processed = 0;
231 parsed_values.emplace_back(std::stod(str, &characters_processed));
232 AssertThrow(characters_processed == str.size(), ExcFailedToConvertStringToNumber(str));
233 }
234
235 // particle location
236 dealii::Point<dim, number> particle_location;
237 for (unsigned int i = 0; i < dim; ++i)
238 particle_location[i] = parsed_values[PropertyCSVColumns::position + i];
239
240 // particle properties
241 std::vector<number> particle_properties(SphericalParticle<dim, number>::n_obstacle_properties,
242 0.0);
243
245 particle_location,
246 particle_properties,
247 dealii::numbers::invalid_unsigned_int /* dummy particle id */);
248
250 parsed_values[PropertyCSVColumns::radius],
251 parsed_values[PropertyCSVColumns::density]);
252
253 return std::optional<std::pair<std::vector<number>, dealii::Point<dim, number>>>(
254 {particle_properties, particle_location});
255 };
256
257 std::vector<dealii::Point<dim, number>> particle_locations;
258 std::vector<std::vector<number>> particle_properties;
259
260 if (dealii::Utilities::MPI::this_mpi_process(mpi_communicator) == 0)
261 {
262 std::fstream file(filename);
263 AssertThrow(!(file.fail()),
264 dealii::ExcMessage("Unable to open particle data file \"" + filename +
265 "\". Aborting!"));
266 std::string line;
267 std::getline(file, line);
268
269 const auto throw_error_with_line_info = [&](const unsigned int line_number,
270 const std::string &error_message) {
271 AssertThrow(false,
272 dealii::ExcMessage("Error parsing line " + std::to_string(line_number) +
273 " in particle data file: " + error_message));
274 };
275
276 unsigned int line_number = 2; // Start from 2 to account for the header line
277 while (std::getline(file, line))
278 {
279 try
280 {
281 const std::optional<std::pair<std::vector<number>, dealii::Point<dim, number>>>
282 obstacle_properties = read_values_from_line(line);
283 if (obstacle_properties.has_value())
284 {
285 particle_properties.push_back(obstacle_properties.value().first);
286 particle_locations.push_back(obstacle_properties.value().second);
287 }
288 ++line_number;
289 }
290 catch (const ExcInvalidCSVInputColumns &e)
291 {
292 throw_error_with_line_info(line_number, e.what());
293 }
294 catch (const ExcFailedToConvertStringToNumber &e)
295 {
296 throw_error_with_line_info(line_number, e.what());
297 }
298 }
299 }
300
301 return {std::move(particle_locations), std::move(particle_properties)};
302 }
303} // namespace MeltPoolDG
Definition particle_accessor.hpp:20
number & angular_velocity(const unsigned int dimension)
Definition particle_accessor.hpp:455
dealii::Point< dim, number > & get_location()
Definition particle_accessor.hpp:376
dealii::Tensor< 1, dim, number > linear_velocity() const
Definition particle_accessor.hpp:390
number & get_property(const typename SphericalParticle< dim, number >::Properties property)
Definition particle_accessor.hpp:628
Class representing a finite-sized spherical particle, intended for use with the obstacle field class.
Definition particle.hpp:35
Interface for a general preconditioner.
Definition boundary_condition_functions.hpp:17
number compute_spherical_particle_volume(const number radius)
Definition particle_tools.hpp:31
void compute_and_set_spherical_particle_properties(DEMParticleAccessor< dim, number > accessor, const number radius, const number density)
Definition particle_tools.hpp:87
std::pair< std::vector< dealii::Point< dim, number > >, std::vector< std::vector< number > > > read_particle_state_input_file(const std::string &filename, const MPI_Comm &mpi_communicator)
Definition particle_tools.hpp:168
dealii::Tensor< 1, dim, VectorizedArrayType > vector_to_center_of_gravity(const DEMParticleAccessor< dim, number > &particle, const dealii::Point< dim, VectorizedArrayType > &location)
Definition particle_tools.hpp:106
dealii::Tensor< 1, dim, VectorizedArrayType > local_particle_velocity(const DEMParticleAccessor< dim, number > &particle, const dealii::Point< dim, VectorizedArrayType > &location)
Definition particle_tools.hpp:119
number compute_spherical_particle_moment_of_inertia(const number mass, const number radius)
Definition particle_tools.hpp:59