/* SPDX-FileCopyrightText: Copyright (c) 2016 - 2026, the adamantine authors. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ #ifndef THERMAL_OPERATOR_TEMPLATES_HH #define THERMAL_OPERATOR_TEMPLATES_HH #include #include #include #include #include #include #include #include #include #include #include #include #include namespace adamantine { template ThermalOperator:: ThermalOperator( MPI_Comm const &communicator, Boundary const &boundary, MaterialProperty &material_properties, std::vector>> const &heat_sources) : _communicator(communicator), _boundary(boundary), _material_properties(material_properties), _heat_sources(heat_sources), _inverse_mass_matrix( new dealii::LA::distributed::Vector()) { _adiabatic_only_bc = _boundary.get_boundary_ids(BoundaryType::adiabatic).size() == _boundary.n_boundary_ids(); _matrix_free_data.tasks_parallel_scheme = dealii::MatrixFree::AdditionalData::partition_color; _matrix_free_data.mapping_update_flags = dealii::update_values | dealii::update_gradients | dealii::update_JxW_values | dealii::update_quadrature_points; _matrix_free_data.mapping_update_flags_inner_faces = dealii::update_values | dealii::update_JxW_values; _matrix_free_data.mapping_update_flags_boundary_faces = dealii::update_values | dealii::update_JxW_values; } template void ThermalOperator:: reinit(dealii::DoFHandler const &dof_handler, dealii::AffineConstraints const &affine_constraints, dealii::hp::QCollection<1> const &q_collection) { _matrix_free.reinit(dealii::StaticMappingQ1::mapping, dof_handler, affine_constraints, q_collection, _matrix_free_data); _affine_constraints = &affine_constraints; // Compute mapping between DoFHandler cells and the MatrixFree cells _cell_it_to_mf_cell_map.clear(); unsigned int const n_cells = _matrix_free.n_cell_batches(); for (unsigned int cell = 0; cell < n_cells; ++cell) for (unsigned int i = 0; i < _matrix_free.n_active_entries_per_cell_batch(cell); ++i) { typename dealii::DoFHandler::cell_iterator cell_it = _matrix_free.get_cell_iterator(cell, i); _cell_it_to_mf_cell_map[cell_it] = std::make_pair(cell, i); } } template void ThermalOperator:: cell_local_mass( dealii::MatrixFree const &data, dealii::LA::distributed::Vector &dst, dealii::LA::distributed::Vector const &src, std::pair const &cell_range) const { // Get the subrange of cells associated with the fe index 0 std::pair cell_subrange = data.create_cell_subrange_hp_by_index(cell_range, 0); dealii::FEEvaluation fe_eval(data); // Loop over the "cells". Note that we don't really work on a cell but on a // set of quadrature point. for (unsigned int cell = cell_subrange.first; cell < cell_subrange.second; ++cell) { // Reinit fe_eval on the current cell fe_eval.reinit(cell); // Store in a local vector the local values of src fe_eval.read_dof_values(src); // Evaluate the shape function on the reference cell fe_eval.evaluate(dealii::EvaluationFlags::values); // Apply the Jacobian of the transformation for (unsigned int q = 0; q < fe_eval.n_q_points; ++q) { fe_eval.submit_value(fe_eval.get_value(q), q); } // Sum over the quadrature points. fe_eval.integrate(dealii::EvaluationFlags::values); fe_eval.distribute_local_to_global(dst); } } template void ThermalOperator:: compute_inverse_mass_matrix( dealii::DoFHandler const &dof_handler, dealii::AffineConstraints const &affine_constraints) { // Compute the inverse of the mass matrix dealii::hp::QCollection mass_q_collection; mass_q_collection.push_back(dealii::QGaussLobatto(fe_degree + 1)); mass_q_collection.push_back(dealii::QGaussLobatto(2)); typename dealii::MatrixFree::AdditionalData mass_matrix_free_data; mass_matrix_free_data.tasks_parallel_scheme = dealii::MatrixFree::AdditionalData::partition_color; mass_matrix_free_data.mapping_update_flags = dealii::update_values | dealii::update_JxW_values; dealii::MatrixFree mass_matrix_free; mass_matrix_free.reinit(dealii::StaticMappingQ1::mapping, dof_handler, affine_constraints, mass_q_collection, mass_matrix_free_data); mass_matrix_free.initialize_dof_vector(*_inverse_mass_matrix); dealii::LA::distributed::Vector unit_vector; mass_matrix_free.initialize_dof_vector(unit_vector); unit_vector = 1.; mass_matrix_free.cell_loop(&ThermalOperator::cell_local_mass, this, *_inverse_mass_matrix, unit_vector); // Because cell_loop resolves the constraints, the constrained dofs are not // called they stay at zero. Thus, we need to force the value on the // constrained dofs by hand. std::vector const &constrained_dofs = mass_matrix_free.get_constrained_dofs(); for (auto &dof : constrained_dofs) _inverse_mass_matrix->local_element(dof) += 1.; _inverse_mass_matrix->compress(dealii::VectorOperation::add); unsigned int const local_size = _inverse_mass_matrix->locally_owned_size(); for (unsigned int k = 0; k < local_size; ++k) { _inverse_mass_matrix->local_element(k) = 1. / _inverse_mass_matrix->local_element(k); } } template void ThermalOperator::clear() { _cell_it_to_mf_cell_map.clear(); _matrix_free.clear(); _inverse_mass_matrix->reinit(0); } template void ThermalOperator:: vmult(dealii::LA::distributed::Vector &dst, dealii::LA::distributed::Vector const &src) const { dst = 0.; vmult_add(dst, src); } template void ThermalOperator:: vmult_add(dealii::LA::distributed::Vector &dst, dealii::LA::distributed::Vector const &src) const { // Execute the matrix-free matrix-vector multiplication // If we use adiabatic boundary condition, we have nothing to do on the faces // of the cell if (_adiabatic_only_bc) { _matrix_free.cell_loop(&ThermalOperator::cell_local_apply, this, dst, src); } else { // MatrixFree::loop works like cell_loop but also allow computation on // internal faces and boundary faces. Here, we use the same function for // both cases and apply the face condition only at the boundary of the // activated domain. _matrix_free.loop(&ThermalOperator::cell_local_apply, &ThermalOperator::face_local_apply, &ThermalOperator::face_local_apply, this, dst, src); } // Because cell_loop resolves the constraints, the constrained dofs are not // called they stay at zero. Thus, we need to force the value on the // constrained dofs by hand. The variable scaling is used so that we get the // right order of magnitude. // TODO: for now the value of scaling is set to 1 double const scaling = 1.; std::vector const &constrained_dofs = _matrix_free.get_constrained_dofs(); for (auto &dof : constrained_dofs) dst.local_element(dof) += scaling * src.local_element(dof); } template void ThermalOperator:: update_state_ratios( [[maybe_unused]] unsigned int cell, [[maybe_unused]] unsigned int q, [[maybe_unused]] dealii::VectorizedArray temperature, std::array, MaterialStates::n_material_states> &state_ratios) const { unsigned int constexpr solid = static_cast(MaterialStates::State::solid); if constexpr (std::is_same_v) { state_ratios[solid] = 1.; } else if constexpr (std::is_same_v) { unsigned int constexpr liquid = static_cast(MaterialStates::State::liquid); // Loop over the vectorized arrays for (unsigned int n = 0; n < temperature.size(); ++n) { // Get the material id at this point dealii::types::material_id const material_id = _material_id(cell, q)[n]; // Get the material thermodynamic properties double const solidus = _material_properties.get(material_id, Property::solidus); double const liquidus = _material_properties.get(material_id, Property::liquidus); // Update the state ratios if (temperature[n] < solidus) state_ratios[liquid][n] = 0.; else if (temperature[n] > liquidus) state_ratios[liquid][n] = 1.; else { state_ratios[liquid][n] = (temperature[n] - solidus) / (liquidus - solidus); } state_ratios[solid][n] = 1. - state_ratios[liquid][n]; } _liquid_ratio(cell, q) = state_ratios[liquid]; } else if constexpr (std::is_same_v) { unsigned int constexpr liquid = static_cast(MaterialStates::State::liquid); unsigned int constexpr powder = static_cast(MaterialStates::State::powder); // Loop over the vectorized arrays for (unsigned int n = 0; n < temperature.size(); ++n) { // Get the material id at this point dealii::types::material_id const material_id = _material_id(cell, q)[n]; // Get the material thermodynamic properties double const solidus = _material_properties.get(material_id, Property::solidus); double const liquidus = _material_properties.get(material_id, Property::liquidus); // Update the state ratios state_ratios[powder] = _powder_ratio(cell, q); if (temperature[n] < solidus) state_ratios[liquid][n] = 0.; else if (temperature[n] > liquidus) state_ratios[liquid][n] = 1.; else { state_ratios[liquid][n] = (temperature[n] - solidus) / (liquidus - solidus); } // Because the powder can only become liquid, the solid can only // become liquid, and the liquid can only become solid, the ratio of // powder can only decrease. state_ratios[powder][n] = std::min(1. - state_ratios[liquid][n], state_ratios[powder][n]); state_ratios[solid][n] = 1. - state_ratios[liquid][n] - state_ratios[powder][n]; } _liquid_ratio(cell, q) = state_ratios[liquid]; _powder_ratio(cell, q) = state_ratios[powder]; } } template void ThermalOperator:: update_face_state_ratios( [[maybe_unused]] unsigned int face, [[maybe_unused]] unsigned int q, [[maybe_unused]] dealii::VectorizedArray temperature, std::array, MaterialStates::n_material_states> &face_state_ratios) const { unsigned int constexpr solid = static_cast(MaterialStates::State::solid); if constexpr (std::is_same_v) { // We just nee to fill state_ratios with 1. for (unsigned int n = 0; n < face_state_ratios[solid].size(); ++n) { face_state_ratios[solid][n] = 1.; } } else if constexpr (std::is_same_v) { unsigned int constexpr liquid = static_cast(MaterialStates::State::liquid); // Loop over the vectorized arrays for (unsigned int n = 0; n < temperature.size(); ++n) { // Get the material id at this point dealii::types::material_id const material_id = _face_material_id(face, q)[n]; // Get the material thermodynamic properties double const solidus = _material_properties.get(material_id, Property::solidus); double const liquidus = _material_properties.get(material_id, Property::liquidus); // Update the state ratios if (temperature[n] < solidus) face_state_ratios[liquid][n] = 0.; else if (temperature[n] > liquidus) face_state_ratios[liquid][n] = 1.; else { face_state_ratios[liquid][n] = (temperature[n] - solidus) / (liquidus - solidus); } face_state_ratios[solid][n] = 1. - face_state_ratios[liquid][n]; } } else if constexpr (std::is_same_v) { unsigned int constexpr liquid = static_cast(MaterialStates::State::liquid); unsigned int constexpr powder = static_cast(MaterialStates::State::powder); // Loop over the vectorized arrays for (unsigned int n = 0; n < temperature.size(); ++n) { // Get the material id at this point dealii::types::material_id const material_id = _face_material_id(face, q)[n]; // Get the material thermodynamic properties double const solidus = _material_properties.get(material_id, Property::solidus); double const liquidus = _material_properties.get(material_id, Property::liquidus); // Update the state ratios face_state_ratios[powder] = _face_powder_ratio(face, q); if (temperature[n] < solidus) face_state_ratios[liquid][n] = 0.; else if (temperature[n] > liquidus) face_state_ratios[liquid][n] = 1.; else { face_state_ratios[liquid][n] = (temperature[n] - solidus) / (liquidus - solidus); } // Because the powder can only become liquid, the solid can only // become liquid, and the liquid can only become solid, the ratio of // powder can only decrease. face_state_ratios[powder][n] = std::min(1. - face_state_ratios[liquid][n], face_state_ratios[powder][n]); face_state_ratios[solid][n] = 1. - face_state_ratios[liquid][n] - face_state_ratios[powder][n]; } _face_powder_ratio(face, q) = face_state_ratios[powder]; } } template dealii::VectorizedArray ThermalOperator:: get_inv_rho_cp( std::array::size()> const &material_id, std::array, MaterialStates::n_material_states> const &state_ratios, dealii::VectorizedArray const &temperature, dealii::AlignedVector> const &temperature_powers) const { // Here we need the specific heat (including the latent heat contribution) // and the density // Compute the state-dependent properties dealii::VectorizedArray density = _material_properties.template compute_material_property< use_table, StateProperty::density>(material_id, state_ratios, temperature, temperature_powers); dealii::VectorizedArray specific_heat = _material_properties.template compute_material_property< use_table, StateProperty::specific_heat>( material_id, state_ratios, temperature, temperature_powers); // Add in the latent heat contribution if constexpr (!std::is_same_v) { // Get the state-independent material properties dealii::VectorizedArray solidus, liquidus, latent_heat; for (unsigned int n = 0; n < solidus.size(); ++n) { solidus[n] = _material_properties.get(material_id[n], Property::solidus); liquidus[n] = _material_properties.get(material_id[n], Property::liquidus); latent_heat[n] = _material_properties.get(material_id[n], Property::latent_heat); } unsigned int constexpr solid = static_cast(MaterialStates::State::solid); unsigned int constexpr liquid = static_cast(MaterialStates::State::liquid); // We only need to take the latent heat into account if both the liquid and // the solid phases are present. We could use an if with two conditions but // that is very slow. Instead, we create a new variable is_mushy that is // non-zero when there is both solid and liquid. auto is_mushy = state_ratios[liquid] * state_ratios[solid]; for (unsigned int n = 0; n < specific_heat.size(); ++n) { if (is_mushy[n] > 0.0) { specific_heat[n] += latent_heat[n] / (liquidus[n] - solidus[n]); } } } return 1.0 / (density * specific_heat); } template void ThermalOperator:: cell_local_apply( dealii::MatrixFree const &data, dealii::LA::distributed::Vector &dst, dealii::LA::distributed::Vector const &src, std::pair const &cell_range) const { // Get the subrange of cells associated with the fe index 0 std::pair cell_subrange = data.create_cell_subrange_hp_by_index(cell_range, 0); auto const integration_flags = _heat_sources_on ? dealii::EvaluationFlags::values | dealii::EvaluationFlags::gradients : dealii::EvaluationFlags::gradients; dealii::FEEvaluation fe_eval(data); std::array, MaterialStates::n_material_states> state_ratios; // We need powers of temperature to compute the material properties. We // could compute it in MaterialProperty but because it's in a hot loop. // It's really worth to compute it once and pass it when we compute a // material property. dealii::AlignedVector> temperature_powers( p_order + 1); // Loop over the "cells". Note that we don't really work on a cell but on a // set of quadrature point. for (unsigned int cell = cell_subrange.first; cell < cell_subrange.second; ++cell) { // Reinit fe_eval on the current cell fe_eval.reinit(cell); // Store in a local vector the local values of src fe_eval.read_dof_values(src); // Evaluate the function and its gradient on the reference cell fe_eval.evaluate(dealii::EvaluationFlags::values | dealii::EvaluationFlags::gradients); // Apply the Jacobian of the transformation, multiply by the variable // coefficients and the quadrature points for (unsigned int q = 0; q < fe_eval.n_q_points; ++q) { auto temperature = fe_eval.get_value(q); // Precompute the powers of temperature. temperature_powers[0] = 1.0; for (unsigned int i = 1; i <= p_order; ++i) { temperature_powers[i] = temperature_powers[i - 1] * temperature; } // Calculate the local material properties update_state_ratios(cell, q, temperature, state_ratios); auto material_id = _material_id(cell, q); auto inv_rho_cp = get_inv_rho_cp(material_id, state_ratios, temperature, temperature_powers); auto th_conductivity_grad = fe_eval.get_gradient(q); // In 2D we only use x and z, and there are no deposition angle if constexpr (dim == 2) { th_conductivity_grad[axis::x] *= _material_properties.template compute_material_property< use_table, StateProperty::thermal_conductivity_x>( material_id, state_ratios, temperature, temperature_powers); th_conductivity_grad[axis::z] *= _material_properties.template compute_material_property< use_table, StateProperty::thermal_conductivity_z>( material_id, state_ratios, temperature, temperature_powers); } if constexpr (dim == 3) { auto const th_conductivity_grad_x = th_conductivity_grad[axis::x]; auto const th_conductivity_grad_y = th_conductivity_grad[axis::y]; auto const thermal_conductivity_x = _material_properties.template compute_material_property< use_table, StateProperty::thermal_conductivity_x>( material_id, state_ratios, temperature, temperature_powers); auto const thermal_conductivity_y = _material_properties.template compute_material_property< use_table, StateProperty::thermal_conductivity_y>( material_id, state_ratios, temperature, temperature_powers); auto cos = _deposition_cos(cell, q); auto sin = _deposition_sin(cell, q); // The rotation is performed using the following formula // // (cos -sin) (x 0) ( cos sin) // (sin cos) (0 y) (-sin cos) // = // ((x*cos^2 + y*sin^2) ((x-y) * (sin*cos))) // (((x-y) * (sin*cos)) (x*sin^2 + y*cos^2)) th_conductivity_grad[axis::x] = (thermal_conductivity_x * cos * cos + thermal_conductivity_y * sin * sin) * th_conductivity_grad_x + ((thermal_conductivity_x - thermal_conductivity_y) * sin * cos) * th_conductivity_grad_y; th_conductivity_grad[axis::y] = ((thermal_conductivity_x - thermal_conductivity_y) * sin * cos) * th_conductivity_grad_x + (thermal_conductivity_x * sin * sin + thermal_conductivity_y * cos * cos) * th_conductivity_grad_y; // There is no deposition angle for the z axis th_conductivity_grad[axis::z] *= _material_properties.template compute_material_property< use_table, StateProperty::thermal_conductivity_z>( material_id, state_ratios, temperature, temperature_powers); } fe_eval.submit_gradient(-inv_rho_cp * th_conductivity_grad, q); // Compute source term if (_heat_sources_on) { dealii::Point> const &q_point = fe_eval.quadrature_point(q); dealii::VectorizedArray quad_pt_source = 0.0; for (auto &beam : _heat_sources) quad_pt_source += beam->value(q_point); quad_pt_source *= inv_rho_cp; fe_eval.submit_value(quad_pt_source, q); } } // Sum over the quadrature points. fe_eval.integrate(integration_flags); fe_eval.distribute_local_to_global(dst); } } template void ThermalOperator:: face_local_apply( dealii::MatrixFree const &data, dealii::LA::distributed::Vector &dst, dealii::LA::distributed::Vector const &src, std::pair const &face_range) const { // Get the fe_indices of the cells that share faces in face_range; auto const adjacent_cells_fe_index = data.get_face_range_category(face_range); // We now have four cases: // - cell_1 = cell_2 = FE_Q: internal face of the activated domain // - cell_1/2 = FE_Q and cell_2/1 = FE_Nothing/does not exit: boundary of // the // activated domain // - cell_1/2 = FE_Nothing and cell_2/1 = does not exit: external boundary // of // the deactivated domain // - cell_1 = cell_2 = FE_Nothing: internal face of the non-activated // domain // Since we only care on the faces that are at the boundary of the activated // domain, we need to check that cell_1 is different than cell_2 and that at // one of the two cells is using FE_Q if (adjacent_cells_fe_index.first == adjacent_cells_fe_index.second) { return; } if ((adjacent_cells_fe_index.first != 0 && adjacent_cells_fe_index.second != 0)) { return; } // Create the FEFaceEvaluation object. The boolean in the constructor is // used to decided which cell the face should be exterior to. dealii::FEFaceEvaluation fe_face_eval(data, adjacent_cells_fe_index.first == 0); std::array, MaterialStates::n_material_states> face_state_ratios; // Create variables used to compute boundary conditions. auto conv_temperature_infty = dealii::make_vectorized_array(0.); auto conv_heat_transfer_coef = dealii::make_vectorized_array(0.); auto rad_temperature_infty = dealii::make_vectorized_array(0.); auto rad_heat_transfer_coef = dealii::make_vectorized_array(0.); // We need powers of temperature to compute the material properties. We // could compute it in MaterialProperty but because it's in a hot loop, // it's really worth to compute it once and pass it when we compute a // material property. dealii::AlignedVector> temperature_powers( p_order + 1); // Loop over the faces for (unsigned int face = face_range.first; face < face_range.second; ++face) { conv_heat_transfer_coef = 0.; rad_heat_transfer_coef = 0.; // Reinit fe_face_eval on the current face fe_face_eval.reinit(face); // Get the boundary type BoundaryType const boundary_type = _boundary.get_boundary_type(fe_face_eval.boundary_id()); // Store in a local vector the local values of src fe_face_eval.read_dof_values(src); // Evalue the function on the reference cell fe_face_eval.evaluate(dealii::EvaluationFlags::values); // Apply the Jacobian of the transformation, mutliply by the variable // coefficients and the quadrature points for (unsigned int q = 0; q < fe_face_eval.n_q_points; ++q) { auto temperature = fe_face_eval.get_value(q); // Precompute the powers of temperature. temperature_powers[0] = 1.0; for (unsigned int i = 1; i <= p_order; ++i) { temperature_powers[i] = temperature_powers[i - 1] * temperature; } // Compute the local_properties auto material_id = _face_material_id(face, q); update_face_state_ratios(face, q, temperature, face_state_ratios); auto const inv_rho_cp = get_inv_rho_cp(material_id, face_state_ratios, temperature, temperature_powers); if (boundary_type & BoundaryType::convective) { for (unsigned int n = 0; n < conv_temperature_infty.size(); ++n) { conv_temperature_infty[n] = _material_properties.get( material_id[n], Property::convection_temperature_infty); } conv_heat_transfer_coef = _material_properties.template compute_material_property< use_table, StateProperty::convection_heat_transfer_coef>( material_id, face_state_ratios, temperature, temperature_powers); } if (boundary_type & BoundaryType::radiative) { for (unsigned int n = 0; n < rad_temperature_infty.size(); ++n) { rad_temperature_infty[n] = _material_properties.get( material_id[n], Property::radiation_temperature_infty); } // We need the radiation heat transfer coefficient but it is not a // real material property but it is derived from other material // properties: h_rad = emissitivity * stefan-boltzmann constant * (T // + T_infty) (T^2 + T^2_infty). rad_heat_transfer_coef = _material_properties.template compute_material_property< use_table, StateProperty::emissivity>( material_id, face_state_ratios, temperature, temperature_powers) * Constant::stefan_boltzmann * (temperature + rad_temperature_infty) * (temperature * temperature + rad_temperature_infty * rad_temperature_infty); } auto const boundary_val = -inv_rho_cp * (conv_heat_transfer_coef * (temperature - conv_temperature_infty) + rad_heat_transfer_coef * (temperature - rad_temperature_infty)); fe_face_eval.submit_value(boundary_val, q); } // Sum over the quadrature points fe_face_eval.integrate(dealii::EvaluationFlags::values); fe_face_eval.distribute_local_to_global(dst); } } template void ThermalOperator::get_state_from_material_properties() { unsigned int const n_cells = _matrix_free.n_cell_batches(); dealii::FEEvaluation fe_eval( _matrix_free); if constexpr (!std::is_same_v) { _liquid_ratio.reinit(n_cells, fe_eval.n_q_points); } if constexpr (std::is_same_v) { _powder_ratio.reinit(n_cells, fe_eval.n_q_points); } _material_id.reinit(n_cells, fe_eval.n_q_points); for (unsigned int cell = 0; cell < n_cells; ++cell) for (unsigned int q = 0; q < fe_eval.n_q_points; ++q) for (unsigned int i = 0; i < _matrix_free.n_active_entries_per_cell_batch(cell); ++i) { typename dealii::DoFHandler::cell_iterator cell_it = _matrix_free.get_cell_iterator(cell, i); // Cast to Triangulation::cell_iterator to access the material_id typename dealii::Triangulation::active_cell_iterator cell_tria( cell_it); if constexpr (!std::is_same_v) { _liquid_ratio(cell, q)[i] = _material_properties.get_state_ratio( cell_tria, MaterialStates::State::liquid); } if constexpr (std::is_same_v) { _powder_ratio(cell, q)[i] = _material_properties.get_state_ratio( cell_tria, MaterialStates::State::powder); } _material_id(cell, q)[i] = cell_tria->material_id(); } // If we are using boundary conditions other than adiabatic, we also need to // update the face variables if (!_adiabatic_only_bc) { unsigned int const n_inner_faces = _matrix_free.n_inner_face_batches(); unsigned int const n_boundary_faces = _matrix_free.n_boundary_face_batches(); unsigned int const n_faces = n_inner_faces + n_boundary_faces; dealii::FEFaceEvaluation fe_face_eval(_matrix_free, true); if constexpr (std::is_same_v) { _face_powder_ratio.reinit(n_faces, fe_face_eval.n_q_points); } _face_material_id.reinit(n_faces, fe_face_eval.n_q_points); for (unsigned int face = 0; face < n_inner_faces; ++face) for (unsigned int q = 0; q < fe_face_eval.n_q_points; ++q) for (unsigned int i = 0; i < _matrix_free.n_active_entries_per_face_batch(face); ++i) { // We get the two cells associated with the face auto [cell_1, face_1] = _matrix_free.get_face_iterator(face, i, true); auto [cell_2, face_2] = _matrix_free.get_face_iterator(face, i, false); // We only care for cells that are at the boundary between activated // and deactivated domains unsigned int const active_fe_index_1 = cell_1->active_fe_index(); unsigned int const active_fe_index_2 = cell_2->active_fe_index(); if (active_fe_index_1 == active_fe_index_2) { continue; } // We need the cell that has FE_Q not the one that has FE_Nothing // Cast to Triangulation::cell_iterator to access the // material_id typename dealii::Triangulation::active_cell_iterator cell_tria( (active_fe_index_1 == 0) ? cell_1 : cell_2); if (cell_tria->is_locally_owned()) { if constexpr (std::is_same_v) { _face_powder_ratio(face, q)[i] = _material_properties.get_state_ratio( cell_tria, MaterialStates::State::powder); } _face_material_id(face, q)[i] = cell_tria->material_id(); } } for (unsigned int face = n_inner_faces; face < n_faces; ++face) for (unsigned int q = 0; q < fe_face_eval.n_q_points; ++q) for (unsigned int i = 0; i < _matrix_free.n_active_entries_per_face_batch(face); ++i) { // We get one cell associated with the face auto [cell, face_] = _matrix_free.get_face_iterator(face, i, true); unsigned int const active_fe_index = cell->active_fe_index(); if (active_fe_index == 1) { continue; } // We need the cell that has FE_Q not the one that has FE_Nothing // Cast to Triangulation::cell_iterator to access the // material_id typename dealii::Triangulation::active_cell_iterator cell_tria( cell); if (cell_tria->is_locally_owned()) { if constexpr (std::is_same_v) { _face_powder_ratio(face, q)[i] = _material_properties.get_state_ratio( cell_tria, MaterialStates::State::powder); } _face_material_id(face, q)[i] = cell_tria->material_id(); } } } } template void ThermalOperator::set_state_to_material_properties() { _material_properties.set_state(_liquid_ratio, _powder_ratio, _cell_it_to_mf_cell_map, _matrix_free.get_dof_handler()); } template void ThermalOperator:: set_material_deposition_orientation( std::vector const &deposition_cos, std::vector const &deposition_sin) { unsigned int const n_cells = _matrix_free.n_cell_batches(); dealii::FEEvaluation fe_eval( _matrix_free); _deposition_cos.reinit(n_cells, fe_eval.n_q_points); _deposition_sin.reinit(n_cells, fe_eval.n_q_points); using dof_cell_iterator = typename dealii::DoFHandler::cell_iterator; std::map cell_mapping; unsigned int pos = 0; for (auto const &cell : dealii::filter_iterators( _matrix_free.get_dof_handler().active_cell_iterators(), dealii::IteratorFilters::LocallyOwnedCell(), dealii::IteratorFilters::ActiveFEIndexEqualTo(0))) { cell_mapping[cell] = pos; ++pos; } ASSERT((pos == 0) || (pos - 1 < deposition_cos.size()), "Out-of-bound access."); for (unsigned int cell = 0; cell < n_cells; ++cell) for (unsigned int q = 0; q < fe_eval.n_q_points; ++q) for (unsigned int i = 0; i < _matrix_free.n_active_entries_per_cell_batch(cell); ++i) { dof_cell_iterator cell_it = _matrix_free.get_cell_iterator(cell, i); if (cell_it->active_fe_index() == 0) { unsigned int const j = cell_mapping[cell_it]; _deposition_cos(cell, q)[i] = deposition_cos[j]; _deposition_sin(cell, q)[i] = deposition_sin[j]; } } } } // namespace adamantine #endif