Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(Adamantine_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/DataAssimilator.hh
${CMAKE_CURRENT_SOURCE_DIR}/ElectronBeamHeatSource.hh
${CMAKE_CURRENT_SOURCE_DIR}/ExperimentalData.hh
${CMAKE_CURRENT_SOURCE_DIR}/GaussianHeatSource.hh
${CMAKE_CURRENT_SOURCE_DIR}/Geometry.hh
${CMAKE_CURRENT_SOURCE_DIR}/GoldakHeatSource.hh
${CMAKE_CURRENT_SOURCE_DIR}/HeatSource.hh
Expand Down Expand Up @@ -42,6 +43,7 @@ set(Adamantine_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/CubeHeatSource.cc
${CMAKE_CURRENT_SOURCE_DIR}/DataAssimilator.cc
${CMAKE_CURRENT_SOURCE_DIR}/ElectronBeamHeatSource.cc
${CMAKE_CURRENT_SOURCE_DIR}/GaussianHeatSource.cc
${CMAKE_CURRENT_SOURCE_DIR}/Geometry.cc
${CMAKE_CURRENT_SOURCE_DIR}/GoldakHeatSource.cc
${CMAKE_CURRENT_SOURCE_DIR}/MaterialPropertyInstDev.cc
Expand Down
213 changes: 213 additions & 0 deletions source/GaussianHeatSource.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/* SPDX-FileCopyrightText: Copyright (c) 2020 - 2026, the adamantine authors.
Comment thread
colemanjs marked this conversation as resolved.
Outdated
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#include <GaussianHeatSource.hh>
#include <instantiation.hh>
#include <types.hh>

#include <deal.II/base/utilities.h>

namespace adamantine
{

template <int dim>
GaussianHeatSource<dim>::GaussianHeatSource(
boost::property_tree::ptree const &beam_database,
boost::optional<boost::property_tree::ptree const &> const
&units_optional_database)
: HeatSource<dim>(beam_database, units_optional_database),
_five_axis(this->_scan_path.is_five_axis())
{
_A = beam_database.get<double>("A");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you read an input from the property tree, add a comment like:

// PropertyTreeInput sources.beam_X.A

I use that to grep the code and make sure that the documentation is up-to-date

_B = beam_database.get<double>("B");
}

template <int dim>
void GaussianHeatSource<dim>::update_time(double time)
{
double const segment_power_modifier =
this->_scan_path.get_power_modifier(time);
this->_source_on = (segment_power_modifier > 0.0);

dealii::Point<3> const &path = this->_scan_path.value(time);
_quaternion = this->_scan_path.get_current_quaternion();

for (unsigned int d = 0; d < 3; ++d)
_beam_center(d) = path(d);

_depth = this->_beam.depth;
_radius_squared = this->_beam.radius_squared;

double const aspect_ratio = std::max(1.0, _depth[0] / this->_beam.radius);
double const n =
std::min(std::max(0.0, _A * std::log2(aspect_ratio) + _B), 9.0);
_k = std::pow(2.0, n);

double const V0 =
0.5 * dealii::numbers::PI * _radius_squared[0] * _depth[0] *
std::tgamma(1.0 / _k) / (_k * std::pow(3.0, 1.0 / _k));

double const effective_power =
this->_beam.absorption_efficiency * this->_beam.max_power *
segment_power_modifier;

_alpha = effective_power / V0;
}

template <int dim>
double GaussianHeatSource<dim>::value(dealii::Point<dim> const &point) const
{
dealii::Point<dim> rotated_point;
if constexpr (dim == 2)
{
rotated_point = point;
}
else
{
rotated_point = _five_axis ? _quaternion.rotate(point) : point;
}

double const z = rotated_point[axis<dim>::z] - _beam_center[axis<dim>::z][0];
if ((z + _depth[0]) < 0.)
{
return 0.;
}

double xpy_squared = dealii::Utilities::fixed_power<2>(
rotated_point[axis<dim>::x] - _beam_center[axis<dim>::x][0]);
if constexpr (dim == 3)
{
xpy_squared += dealii::Utilities::fixed_power<2>(
rotated_point[axis<dim>::y] - _beam_center[axis<dim>::y][0]);
}

if (xpy_squared > 5. * _radius_squared[0])
{
return 0.;
}
if (std::abs(z) > 5. * _depth[0])
{
return 0.;
}

double const radial_component =
std::exp(-2.0 * xpy_squared / _radius_squared[0]);

double const depth_component =
std::exp(-3.0 * std::pow(std::abs(z) / _depth[0], _k));

return _alpha[0] * radial_component * depth_component;
}

template <int dim>
dealii::VectorizedArray<double> GaussianHeatSource<dim>::value(
dealii::Point<dim, dealii::VectorizedArray<double>> const &points) const
{
dealii::Point<dim, dealii::VectorizedArray<double>> rotated_points;
if constexpr (dim == 2)
{
rotated_points = points;
}
else
{
rotated_points = _five_axis ? _quaternion.rotate(points) : points;
}

auto const z = rotated_points[axis<dim>::z] - _beam_center[axis<dim>::z];
auto const z_depth = z + _depth;

dealii::VectorizedArray<double> depth_mask;
for (unsigned int i = 0; i < depth_mask.size(); ++i)
{
depth_mask[i] = z_depth[i] < 0. ? 0. : 1.;
}
if (depth_mask.sum() == 0)
{
return depth_mask;
}

auto xpy_squared = dealii::Utilities::fixed_power<2>(
rotated_points[axis<dim>::x] - _beam_center[axis<dim>::x]);
if constexpr (dim == 3)
{
auto const y_squared = dealii::Utilities::fixed_power<2>(
rotated_points[axis<dim>::y] - _beam_center[axis<dim>::y]);
xpy_squared += y_squared;
}

auto const xpy_area = 5. * _radius_squared - xpy_squared;
dealii::VectorizedArray<double> xpy_mask;
for (unsigned int i = 0; i < xpy_mask.size(); ++i)
{
xpy_mask[i] = xpy_area[i] < 0. ? 0. : 1.;
}
if (xpy_mask.sum() == 0)
{
return xpy_mask;
}

dealii::VectorizedArray<double> shape = _k;

auto const radial_component =
std::exp(-2.0 * xpy_squared / _radius_squared);

auto const depth_component =
std::exp(-3.0 * std::pow(std::abs(z / _depth), shape));

return depth_mask * _alpha * radial_component * depth_component;
}

template <int dim>
dealii::BoundingBox<dim>
GaussianHeatSource<dim>::get_bounding_box(double const time,
double const scaling_factor) const
{
dealii::Point<3> const &beam_center = this->_scan_path.value(time, false);
if constexpr (dim == 2)
{
return {{{beam_center[axis<dim>::x] - scaling_factor * this->_beam.radius,
beam_center[axis<dim>::z] - scaling_factor * this->_beam.depth},
{beam_center[axis<dim>::x] + scaling_factor * this->_beam.radius,
beam_center[axis<dim>::z]}}};
}
else
{
dealii::Point<3> max_corner(
beam_center[axis<dim>::x] + scaling_factor * this->_beam.radius,
beam_center[axis<dim>::y] + scaling_factor * this->_beam.radius,
beam_center[axis<dim>::z]);
dealii::Point<3> min_corner(
beam_center[axis<dim>::x] - scaling_factor * this->_beam.radius,
beam_center[axis<dim>::y] - scaling_factor * this->_beam.radius,
beam_center[axis<dim>::z] - scaling_factor * this->_beam.depth);

if (this->_scan_path.is_five_axis())
{
dealii::Point<3> rotated_max_corner =
this->_scan_path.rotate(time, max_corner);
dealii::Point<3> rotated_min_corner =
this->_scan_path.rotate(time, min_corner);

dealii::Point<3> new_max_corner;
dealii::Point<3> new_min_corner;
for (int d = 0; d < 3; ++d)
{
new_max_corner[d] =
std::max(rotated_max_corner[d], rotated_min_corner[d]);
new_min_corner[d] =
std::min(rotated_max_corner[d], rotated_min_corner[d]);
}

return {{new_min_corner, new_max_corner}};
}
else
{
return {{min_corner, max_corner}};
}
}
}

} // namespace adamantine

INSTANTIATE_DIM(GaussianHeatSource)
87 changes: 87 additions & 0 deletions source/GaussianHeatSource.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* SPDX-FileCopyrightText: Copyright (c) 2020 - 2026, the adamantine authors.
Comment thread
colemanjs marked this conversation as resolved.
Outdated
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#ifndef GAUSSIAN_HEAT_SOURCE_HH
#define GAUSSIAN_HEAT_SOURCE_HH

#include <HeatSource.hh>
#include <Quaternion.hh>

#include <limits>

namespace adamantine
{
/**
* A derived class from HeatSource for the Gaussian model of a laser heat source.
*/
template <int dim>
class GaussianHeatSource final : public HeatSource<dim>
{
public:
/**
* Constructor.
* \param[in] beam_database requires the following entries:
* - <B>absorption_efficiency</B>: double in \f$[0,1]\f$
* - <B>depth</B>: double in \f$[0,\infty)\f$
* - <B>diameter</B>: double in \f$[0,\infty)\f$
* - <B>max_power</B>: double in \f$[0, \infty)\f$
* - <B>scan_path_file</B>: name of the file that contains the scan path
* segments
* - <B>scan_path_file_format</B>: format of the scan path file
* - <B>A</B>: double, empirical coefficient for the model
* - <B>B</B>: double, empirical coefficient for the model
* \param[in] units_optional_database may contain the following entries:
* - <B>heat_source.dimension</B>
* - <B>heat_source.power</B>
*/
GaussianHeatSource(
boost::property_tree::ptree const &beam_database,
boost::optional<boost::property_tree::ptree const &> const
&units_optional_database);

/**
* Set the time variable. This updates the beam position and recalculates
* the shape factor (_k) and normalization constant (_alpha).
*/
void update_time(double time) final;

/**
* Returns the value of a Gaussian heat source at a specified point.
*/
double value(dealii::Point<dim> const &point) const final;

/**
* Same function as above but it uses vectorized data.
*/
dealii::VectorizedArray<double>
value(dealii::Point<dim, dealii::VectorizedArray<double>> const &points)
const final;

/**
* Returns the bounding box of the heat source at a given time.
*/
dealii::BoundingBox<dim>
get_bounding_box(double time, double const scaling_factor) const final;

private:
double _A;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment here. I have no idea what are A and B

double _B;

bool const _five_axis;
Quaternion _quaternion;

dealii::Point<3, dealii::VectorizedArray<double>> _beam_center;
dealii::VectorizedArray<double> _depth =
std::numeric_limits<double>::signaling_NaN();
dealii::VectorizedArray<double> _radius_squared =
std::numeric_limits<double>::signaling_NaN();

dealii::VectorizedArray<double> _alpha =
std::numeric_limits<double>::signaling_NaN();

double _k = std::numeric_limits<double>::signaling_NaN();
};
} // namespace adamantine

#endif
6 changes: 6 additions & 0 deletions source/ThermalPhysics.templates.hh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#ifndef THERMAL_PHYSICS_TEMPLATES_HH
#define THERMAL_PHYSICS_TEMPLATES_HH

#include <GaussianHeatSource.hh>
#include <CubeHeatSource.hh>
#include <ElectronBeamHeatSource.hh>
#include <GoldakHeatSource.hh>
Expand Down Expand Up @@ -402,6 +403,11 @@ ThermalPhysics<dim, n_materials, p_order, fe_degree, MaterialStates,
_heat_sources[i] = std::make_shared<CubeHeatSource<dim>>(
beam_database, units_optional_database);
}
else if (type == "gaussian")
{
_heat_sources[i] = std::make_shared<GaussianHeatSource<dim>>(
beam_database, units_optional_database);
}
else
{
ASSERT_THROW(false, "Beam type '" +
Expand Down
11 changes: 11 additions & 0 deletions source/ensemble_management.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#include <GaussianHeatSource.hh>
#include <CubeHeatSource.hh>
#include <ElectronBeamHeatSource.hh>
#include <GoldakHeatSource.hh>
Expand Down Expand Up @@ -158,6 +159,16 @@ std::vector<std::shared_ptr<HeatSource<dim>>> get_bounding_heat_sources(
bounding_heat_sources[i] = std::make_shared<CubeHeatSource<dim>>(
beam_database, units_optional_database);
}
else if (type == "gaussian")
{
boost::property_tree::ptree modified_beam_database = beam_database;
modified_beam_database.put("diameter", diameter_max[bounding_source]);
modified_beam_database.put("depth", depth_max[bounding_source]);
++bounding_source;

bounding_heat_sources[i] = std::make_shared<GaussianHeatSource<dim>>(
modified_beam_database, units_optional_database);
}
}

return bounding_heat_sources;
Expand Down
5 changes: 3 additions & 2 deletions source/validate_input_database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,11 @@ void validate_input_database(boost::property_tree::ptree &database)
"sources.beam_" + std::to_string(beam_index) + ".type");
ASSERT_THROW(boost::iequals(beam_type, "goldak") ||
boost::iequals(beam_type, "electron_beam") ||
boost::iequals(beam_type, "cube"),
boost::iequals(beam_type, "cube") ||
boost::iequals(beam_type, "gaussian"),
"Beam type, '" + beam_type +
"', is not recognized. Valid options are: 'goldak', "
"'electron_beam', and 'cube'.");
"'electron_beam', 'cube', and 'gaussian'.");
ASSERT_THROW(database.get_child("sources")
.get_child("beam_" + std::to_string(beam_index))
.count("scan_path_file") != 0,
Expand Down
Loading