Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow different base_elements for compositional fields #5802

Merged
merged 4 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 5 additions & 4 deletions benchmarks/solitary_wave/solitary_wave.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <aspect/postprocess/interface.h>
#include <aspect/gravity_model/interface.h>
#include <aspect/geometry_model/interface.h>
#include <aspect/simulator.h>
#include <aspect/simulator_access.h>
#include <aspect/global.h>

Expand Down Expand Up @@ -828,13 +829,13 @@ namespace aspect
AssertThrow(this->introspection().compositional_name_exists("porosity"),
ExcMessage("Postprocessor Solitary Wave only works if there is a compositional field called porosity."));
const unsigned int porosity_index = this->introspection().compositional_index_for_name("porosity");
const typename Simulator<dim>::AdvectionField porosity = Simulator<dim>::AdvectionField::composition(porosity_index);

// create a quadrature formula based on the compositional element alone.
// be defensive about determining that a compositional field actually exists
AssertThrow (this->introspection().base_elements.compositional_fields
!= numbers::invalid_unsigned_int,
AssertThrow (this->introspection().n_compositional_fields > 0,
ExcMessage("This postprocessor cannot be used without compositional fields."));
const QGauss<dim> quadrature_formula (this->get_fe().base_element(this->introspection().base_elements.compositional_fields).degree+1);

const QGauss<dim> quadrature_formula (this->get_fe().base_element(porosity.base_element(this->introspection())).degree+1);
const unsigned int n_q_points = quadrature_formula.size();

FEValues<dim> fe_values (this->get_mapping(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ namespace aspect
TrenchLocation<dim>::execute (TableHandler &statistics)
{
// create a quadrature formula based on the compositional element alone.
// be defensive about determining that a compositional field actually exists
AssertThrow(this->introspection().base_elements.compositional_fields != numbers::invalid_unsigned_int,
AssertThrow(this->introspection().n_compositional_fields > 0,
ExcMessage("This postprocessor cannot be used without compositional fields."));
const Quadrature<dim - 1> &quadrature_formula = this->introspection().face_quadratures.compositional_fields;

Expand Down
67 changes: 58 additions & 9 deletions include/aspect/introspection.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <aspect/fe_variable_collection.h>
#include <aspect/parameters.h>

#include <map>

namespace aspect
{
Expand Down Expand Up @@ -246,18 +247,17 @@ namespace aspect
* A structure that enumerates the base elements of the finite element
* that correspond to each of the variables in this problem.
*
* If there are compositional fields, they are all discretized with the
* same base element and, consequently, we only need a single index. If
* a variable does not exist in the problem (e.g., we do not have
* compositional fields), then the corresponding index is set to an
* invalid number.
* The indices here can be used to access the dealii::FiniteElement
* that describes the given variable. We support different finite
* elements for compositional fields, but we try to reuse the same
* element if possible.
*/
struct BaseElements
{
unsigned int velocities;
unsigned int pressure;
unsigned int temperature;
unsigned int compositional_fields;
unsigned int velocities;
unsigned int pressure;
unsigned int temperature;
std::vector<unsigned int> compositional_fields;
};

/**
Expand Down Expand Up @@ -441,6 +441,7 @@ namespace aspect
*/
IndexSet locally_owned_fluid_pressure_dofs;
};

/**
* A variable that contains index sets describing which of the globally
* enumerated degrees of freedom are owned by the current processor in a
Expand All @@ -465,6 +466,31 @@ namespace aspect
* @}
*/

/**
* Return a vector that contains the base element indices of the deal.II FiniteElement
* that are used for compositional fields. Note that compositional fields can share the
* same base element, so this vector can (and usually will) be smaller than the number
* of compositional fields. The function get_compositional_field_indices_with_base_element()
* can be used to translate from base element index to all compositional field indices
* using the specified base element.
* If several fields are the finite element type (same degree and both continuous or both
* discontinuous), they share base elements. If you have no compositional fields, the
* vector returned has length 0. If all compositional fields have the same finite element
* space, the length is 1.
*/
std::vector<unsigned int>
get_composition_base_element_indices() const;

/**
* Return a vector with all compositional field indices that belong to a given
* base element index as returned by get_composition_base_element_indices().
* The indices returned are therefore between 0 and n_compositional_fields-1.
* If you have a single compositional field, this function returns {0} when passing
* in base_element_index=0.
*/
std::vector<unsigned int>
get_compositional_field_indices_with_base_element(const unsigned int base_element_index) const;

/**
* A function that gets the name of a compositional field as an input
* parameter and returns its index. If the name is not found, an
Expand Down Expand Up @@ -593,6 +619,16 @@ namespace aspect
bool
is_stokes_component (const unsigned int component_index) const;

/**
* A function that gets a component index as an input
* parameter and returns if the component is one of the
* compositional fields.
*
* @param component_index The component index to check.
*/
bool
is_composition_component (const unsigned int component_index) const;

private:
/**
* A vector that stores the names of the compositional fields that will
Expand Down Expand Up @@ -620,6 +656,19 @@ namespace aspect
* given in CompositionalFieldDescription.
*/
std::vector<std::vector<unsigned int>> composition_indices_for_type;

/**
* List of base element indices used by compositional fields. Cached
* result returned by get_composition_base_element_indices().
*/
std::vector<unsigned int> composition_base_element_indices;

/**
* Map base_element_index to list of compositional field indices that use
* that base element. Cached result returned by
* get_compositional_field_indices_with_base_element();
*/
std::map<unsigned int, std::vector<unsigned int>> compositional_field_indices_with_base_element;
};
}

Expand Down
63 changes: 35 additions & 28 deletions source/mesh_refinement/composition_gradient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/


#include <aspect/simulator.h>
Copy link
Member

Choose a reason for hiding this comment

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

Ah, we really need to find a solution to put AdvectionField out of the simulator.h header. Including simulator.h in a lot of places is not a clean design. Not for this PR though.

Copy link
Member Author

Choose a reason for hiding this comment

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

agreed, see #5887

#include <aspect/mesh_refinement/composition_gradient.h>

#include <deal.II/base/quadrature_lib.h>
Expand All @@ -37,44 +38,50 @@ namespace aspect
"compositional fields are active!"));

indicators = 0;
Vector<float> this_indicator (indicators.size());
const double power = 1.0 + dim/2.0;

const Quadrature<dim> quadrature(this->get_fe().base_element(this->introspection().base_elements.compositional_fields).get_unit_support_points());
FEValues<dim> fe_values (this->get_mapping(),
this->get_fe(),
quadrature,
update_quadrature_points | update_gradients);
for (const unsigned int base_element_index : this->introspection().get_composition_base_element_indices())
{
const Quadrature<dim> quadrature (this->get_fe().base_element(base_element_index).get_unit_support_points());
const unsigned int dofs_per_cell = quadrature.size();
FEValues<dim> fe_values (this->get_mapping(),
this->get_fe(),
quadrature,
update_quadrature_points | update_gradients);

// the values of the compositional fields are stored as block vectors for each field
// we have to extract them in this structure
std::vector<Tensor<1,dim>> composition_gradients (quadrature.size());
// the values of the compositional fields are stored as block vectors for each field
// we have to extract them in this structure
std::vector<Tensor<1,dim>> composition_gradients (quadrature.size());

for (unsigned int c=0; c<this->n_compositional_fields(); ++c)
{
for (const auto &cell : this->get_dof_handler().active_cell_iterators())
if (cell->is_locally_owned())
{
const unsigned int idx = cell->active_cell_index();
fe_values.reinit(cell);
fe_values[this->introspection().extractors.compositional_fields[c]].get_function_gradients (this->get_solution(),
composition_gradients);

// For each composition dof, write into the output vector the
// composition gradient. Note that quadrature points and dofs
// are enumerated in the same order.
for (unsigned int j=0; j<this->get_fe().base_element(this->introspection().base_elements.compositional_fields).dofs_per_cell; ++j)
this_indicator[idx] += composition_gradients[j].norm();

// Scale gradient in each cell with the correct power of h. Otherwise,
// error indicators do not reduce when refined if there is a density
// jump. We need at least order 1 for the error not to grow when
// refining, so anything >1 should work. (note that the gradient
// itself scales like 1/h, so multiplying it with any factor h^s, s>1
// will yield convergence of the error indicators to zero as h->0)
this_indicator[idx] *= std::pow(cell->diameter(), power);

for (const unsigned int c : this->introspection().get_compositional_field_indices_with_base_element(base_element_index))
{
const typename Simulator<dim>::AdvectionField composition = Simulator<dim>::AdvectionField::composition(c);
fe_values[composition.scalar_extractor(this->introspection())].get_function_gradients (this->get_solution(),
composition_gradients);

// Some up the indicators for this composition on this cell. Note that quadrature points and dofs
// are enumerated in the same order.
double this_indicator = 0.0;
for (unsigned int j=0; j<dofs_per_cell; ++j)
this_indicator += composition_gradients[j].norm();

// Scale gradient in each cell with the correct power of h. Otherwise,
// error indicators do not reduce when refined if there is a density
// jump. We need at least order 1 for the error not to grow when
// refining, so anything >1 should work. (note that the gradient
// itself scales like 1/h, so multiplying it with any factor h^s, s>1
// will yield convergence of the error indicators to zero as h->0)
this_indicator *= std::pow(cell->diameter(), power);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this still the same as before? You're not resetting your this_indicator variable in the loop. So it would add up from the different compositional fields with the same base index.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thank you!


indicators[idx] += composition_scaling_factors[c] * this_indicator;
}
}
indicators.add(composition_scaling_factors[c], this_indicator);
}
}

Expand Down
4 changes: 0 additions & 4 deletions source/postprocess/composition_statistics.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ namespace aspect
return {"", ""};

// create a quadrature formula based on the compositional element alone.
// be defensive about determining that a compositional field actually exists
AssertThrow (this->introspection().base_elements.compositional_fields
!= numbers::invalid_unsigned_int,
ExcMessage("This postprocessor cannot be used without compositional fields."));
const Quadrature<dim> &quadrature_formula = this->introspection().quadratures.compositional_fields;
const unsigned int n_q_points = quadrature_formula.size();

Expand Down
2 changes: 1 addition & 1 deletion source/postprocess/max_depth_field.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace aspect
{
// create a quadrature formula based on the compositional element alone.
// be defensive about determining that a compositional field actually exists
AssertThrow(this->introspection().base_elements.compositional_fields != numbers::invalid_unsigned_int,
AssertThrow(this->n_compositional_fields() > 0,
ExcMessage("This postprocessor cannot be used without compositional fields."));
const Quadrature<dim> &quadrature_formula = this->introspection().quadratures.compositional_fields;

Expand Down
Loading