* @brief Concept for term types in the discretization framework. To be a term a type must derive from term_base, must have a field_type member, and that field_type member must be a valid field.
*/
template<typenameT>
conceptTerm=std::derived_from<std::remove_cvref_t<T>,term_base>&&// a term must derive from term_base
requires{typenamestd::remove_cvref_t<T>::field_type;}&&// a term must have a field_type member
Field<typenamestd::remove_cvref_t<T>::field_type>;// That field type member must be a valid field
/**
* @brief Concept for term types that have a functional space. To have a functional space a type must first be a valid @ref serif::discretization::blocks::Term "Term" and must have a FunctionalSpace member type.
* @brief Concept for term types that have a domain over which they are supported. To have support a type must first be a valid @ref serif::discretization::blocks::Term "Term" and must have a Support member type.
* @brief Concept for term types that have a physical quantity associated with them. To have a quantity a type must first be a valid @ref serif::discretization::blocks::Term "Term" and must have a Quantity member type.
* @brief Concept for term types that have a value block associated with them. To have a value block a type must first be a valid @ref serif::discretization::blocks::Term "Term" and must have a value member type.
* @brief Concept for term types that have a residual block associated with them. To have a residual block a type must first be a valid @ref serif::discretization::blocks::Term "Term" and must have a residual member type.
* @brief Concept for term types that are spatial terms. A spatial term is a term that has a functional space (@ref serif::discretization::blocks::HasFunctionalSpace "HasFunctionalSpace") and a support (@ref serif::discretization::blocks::HasSupport "HasSupport").
*/
template<typenameT>
conceptSpatialTerm=
Term<T>&&
HasFunctionalSpace<T>&&
HasSupport<T>;
/**
* @brief Concept for term types that are global scalar terms. A global scalar term is a term that has support (@ref serif::discretization::blocks::HasSupport "HasSupport") but does not have a functional space (@ref serif::discretization::blocks::HasFunctionalSpace "HasFunctionalSpace") and is derived from the global_scalar class.
* @brief Concept for a physical spatial term. A physical spatial term is a spatial term (@ref serif::discretization::blocks::SpatialTerm "SpatialTerm") that has a physical quantity associated with it (@ref serif::discretization::blocks::HasQuantity "HasQuantity").
*/
template<typenameT>
conceptPhysicalSpatialTerm=
SpatialTerm<T>&&
HasQuantity<T>;
/**
* @brief Helper struct to determine if a type is a spatial term (@ref serif::discretization::blocks::SpatialTerm "SpatialTerm").
* @brief Helper struct to determine if a type is a physical spatial term (@ref serif::discretization::blocks::PhysicalSpatialTerm "PhysicalSpatialTerm").
* @brief Define the fields and their associated terms for the discretization. This is a key file to understand if you are to understand SERiF's numerics.
* Each of these field types should contain all the information needed for the compiler to generate the correct
* - blocks
* - functional spaces
* - support domains
* - quantities
* - symbols
* - and any other information needed to generate the correct weak form for the field
* Broadly you can think of these as where we define the "physics" that we want to solve for or the unknowns of the system.
* If you find yourself adding any sort of unknown to the system and you are not adding it to this file you should step back and reconsider\
* if that belongs here instead.
*/
#include<string_view>
#include"serif/dimensions/dimensions.hpp"
@@ -11,120 +27,278 @@
#include"serif/meta/names.hpp"
namespaceserif::discretization::blocks{
/**
* @brief The field struct is a tag type that is used to identify a field in the discretization. It is used to group together terms that belong to the same field. Each field should have a unique name and a list of terms that belong to it.
*/
structfield{};
/**
* @brief The term_base struct is a tag type that is used to identify a term in the discretization. It is used to group together terms that belong to the same field. Each term should have a unique name and a list of blocks that belong to it.
* Generally you should always use term rather than term_base as term associates to a particular field at compile time.
*/
structterm_base{};
/**
* @brief The term struct is a tag type that is used to identify a term in the discretization. It is used to group together terms that belong to the same field. Each term should have a unique name and a list of blocks that belong to it.
* Note that generally you should always use term rather than term_base as term associates to a particular field at compile time.
* @brief A global scalar is a term that has a single value over the entire domain (think angular momentum or total mass). Broadly we need to separate
* these from the other terms since they require different (often less enriched) information at compile time.
*/
structglobal_scalar{
/**
* @brief The value block is a block that contains the value of the global scalar. It is a single value over the entire domain and is not associated with any particular functional space.
* For something like total mass this would be the actual value of the mass of the system
*/
structvaluefinal:value_block_base{
staticconstexprintstatic_block_size=1;///< Because this is a global scalar unknown we can be sure at compile time that the block size is 1 and we can use this to optimize the linear algebra operations on this block.
};
structmassfinal:term<density>{// Note the reading here is mass density (that is this is not in reference to the common FEM usage of mass as a term in a weak form)
/**
* @brief The residual block is a block that contains the residual of the global scalar. It is a single value over the entire domain and is not associated with any particular functional space.
* For something like total mass this would be the residual of the mass of the system (i.e. the difference between the current mass and the target mass).
*/
structresidualfinal:residual_block_base{
staticconstexprintstatic_block_size=1;///< Because this is a global scalar unknown we can be sure at compile time that the block size is 1 and we can use this to optimize the linear algebra operations on this block.
};
};
/**
* @brief The barotropic_constant field is a field that represents the barotropic constant of the system. It is a global scalar that is associated with the mass normalization term. This is used to enforce the mass normalization condition in the weak form when
* we are in the barotropic case. This field will not generalize as useful to the non-barotropic case. Note that we use compile time reflection to get the name of the field and the terms.
*/
structbarotropic_constantfinal:field{
staticconstexprstd::string_viewname=meta::type_name<barotropic_constant>();///< Compile time reflected name of the field
/**
* @brief the mass_normalization term represents the mass normalization condition of the system. That is that it enforces that the total mass of the system is equal to the target mass in a weak sense.
staticconstexprstd::string_viewname=meta::type_name<mass_normalization>();///< Compile time reflected name of the term
usingSupport=domain::StellarDomains;///< The support of the mass normalization term is the stellar domains since we only want to enforce the mass normalization condition in the stellar domains.
usingQuantity=dimensions::SpecificEnthalpy;///< As we are in teh barotropic case the mass normalization term is associated with the specific enthalpy. This will nto generalize to the non-barotropic case.
staticconstexprstd::string_viewsymbol="C";///< Compile time symbol used for the term
};
staticconstexprmass_normalizationmass_normalization_term{};///< Static constexpr instance of the mass_normalization term. This is used to access the term in the discretization.
usingTerms=utils::types::TypeList<mass_normalization>;///< Type list of terms for compile time reflection and iteration over the terms of the field.
};
/**
* @brief The rotational field, represented as the angular velocity term, given that we currently only model rigid rotation this is a global scalar. A future extension which models differential rotation will
* likely need to modify this to some vector field or other more complex representation of the rotation.
*/
structrotationfinal:field{
staticconstexprstd::string_viewname=meta::type_name<rotation>();///< Compile time reflected name of the field
/**
* @brief The angular_velocity term represents the angular velocity of the system. It is a global scalar that is associated with the rotation of the system. This is used to enforce the rotation condition in the weak form when we are in the rigid rotation case.
* Note that downstream the way that this currently works is to calculate the expected angular velocity given the current mass, geometry, density, and angular momentum and then prescribe, given those, what the angular velocity should be.
* Therefore, this does not enforce that the angular velocity must be constant over the entire solver run, but rather that the angular velocity is consistent with the current state of the system.
staticconstexprstd::string_viewname=meta::type_name<angular_velocity>();///< Compile time reflected name of the term
usingSupport=domain::StellarDomains;///< The support of the angular velocity term is the stellar domains since we only want to enforce the rotation condition in the stellar domains.
usingQuantity=dimensions::AngularVelocity;///< As we are in the rigid rotation case the angular velocity term is associated with the angular velocity. This will not generalize to the differential rotation case.
staticconstexprstd::string_viewsymbol="Ω";///< Compile time symbol used for the term
};
staticconstexprangular_velocityangular_velocity_term{};///< Static constexpr instance of the angular_velocity term. This is used to access the term in the discretization.
usingTerms=utils::types::TypeList<angular_velocity>;///< Type list of terms for compile time reflection and iteration over the terms of the field.
};
/**
* @brief field used to inject a border row used to enforce a central density constraint
*/
structcentral_densityfinal:field{
staticconstexprstd::string_viewname=meta::type_name<central_density>();///< Compile time reflected name of the field
/**
* @brief The border term represents the border row used to enforce a central density constraint. This is a global scalar as it is a single value over the entire domain.
staticconstexprstd::string_viewname=meta::type_name<border>();///< Compile time reflected name of the term
usingSupport=domain::StellarDomains;///< We only enforce this over the stellar domain as there is only material to "slosh" around in the stellar domain
usingQuantity=dimensions::SpecificEnthalpy;///< As we are in the barotropic case this border term is associate with the specific enthalpy.
staticconstexprstd::string_viewsymbol="ρ_c";///< Compile time symbol used for the term
};
staticconstexprborderborder_term{};///< Static constexpr instance of the border term. This is used to access the term in the discretization.
usingTerms=utils::types::TypeList<border>;///< Type list of terms for compile time reflection and iteration over the terms of the field.
};
/**
* @brief The density field used to represent the distribution of mass in the system. This is a scalar field that is associated with the mass density of the system. The density field is used to enforce the mass conservation condition in the weak form.
*/
structdensityfinal:field{
staticconstexprstd::string_viewname=meta::type_name<density>();///< Compile time reflected name of the field
/**
* @brief The mass density term for the system. Note that the naming here could be a bit confusing as FEM also uses the term "mass" to represent the canonical mass matrix. This is a more standard physics reading of what is going on here.
* We define that this space is a second order scalar L2 space. This allows for the possibility of discontinuous density fields.
usingFunctionalSpace=functional_space::DiscreteFunctionalSpace<functional_space::L2,2,0>;// second order scalar L2 space
usingSupport=domain::StellarDomains;
usingQuantity=dimensions::MassDensity;
staticconstexprstd::string_viewsymbol="ρ";
usingFunctionalSpace=functional_space::DiscreteFunctionalSpace<functional_space::L2,2,0>;///< second order scalar L2 space. Read the template parameters as follows: 1) L2 space, 2) second order, 3) scalar field (rank 0)
usingSupport=domain::StellarDomains;///< The support of the mass density term is the stellar domains since we do not allow material to exist in the vacuum domain.
usingQuantity=dimensions::MassDensity;///< The quantity of the mass density term is the mass density.
staticconstexprstd::string_viewsymbol="ρ";///< Compile time symbol used for the term. Note that this is a UTF-8 character and may not render correctly in all environments. (TODO: Should we introduce an ASCII fallback?, something like make this a tuple of form <main, fallback>?)
structvaluefinal:value_block_base{};
structresidualfinal:residual_block_base{};
structvaluefinal:value_block_base{};///< The value block is a block that contains the value of the mass density term. It is a scalar field that is associated with the mass density of the system.
structresidualfinal:residual_block_base{};///< The residual block is a block that contains the residual of the mass density term. It is a scalar field that is associated with the mass density of the system.
};
staticinlineconstexprmassmass_density_term{};
usingTerms=utils::types::TypeList<mass>;
staticconstexprmassmass_density_term{};///< Static constexpr instance of the mass term. This is used to access the term in the discretization.
usingTerms=utils::types::TypeList<mass>;///< Type list of terms for compile time reflection and iteration over the terms of the field.
};
/**
* @brief The displacement field is a 3 vector H1 field (C0 continuous) that is used to represent continuous nodal displacement over the entire domain.
* @brief the displacement vector term is a 3 vector H1 field (C0 continuous) that is used to represent continuous nodal displacement over the entire domain.
* This is a vector field that is associated with the mapping of the domain from the reference to physical spaces. E.g. if there is some domain
* O_ref which n nodal positions x^i_ref then there is some other domain O_phys with n nodel positions such that x^i_phys = x^i_ref + d^i where d^i is the displacement vector at node i.
*
* @note Generally a particular model will not actually model its geometry with the full displacement field as an unknown. Rather, the displacement
* system will generally report some surface representation and prescription (say Isobaric with a spherical harmonic expansion) to the solver. So there may be
* say 25 spherical harmonic coefficients for the solver to actually solver for. Those are then analytically mapped to the full displacement field
* using some interior and exterior extension. For more information see the surface and deformation systems.
*/
structvectorfinal:term<displacement>{
staticconstexprstd::string_viewname=meta::type_name<vector>();///< Compile time reflected name of the term
usingFunctionalSpace=functional_space::DiscreteFunctionalSpace<functional_space::H1,3,1>;// third order vector H1 space
usingSupport=domain::AllDomains;
usingQuantity=dimensions::Length;
staticconstexprstd::string_viewsymbol="d";
usingFunctionalSpace=functional_space::DiscreteFunctionalSpace<functional_space::H1,3,1>;///< 3 vector H1 space (C0 continuous). Read the template parameters as follows: 1) H1 space, 2) 3 vector, 3) rank 1 tensor field
usingSupport=domain::AllDomains;///< We support displacement over the entire domain since we want to be able to map the reference domain to the physical domain over the entire domain. This enables both kelvin compactification but also the smooth transition from the stellar domain to the vacuum domain.
usingQuantity=dimensions::Length;///< We represent displacement as a length quantity
staticconstexprstd::string_viewsymbol="d";///< Compile time symbol used for the term.
structvaluefinal:value_block_base{};
structresidualfinal:residual_block_base{};
structvaluefinal:value_block_base{};///< The value block is a block that contains the value of the displacement vector term. It is a vector field that is associated with the mapping of the domain from the reference to physical spaces.
structresidualfinal:residual_block_base{};///< The residual block is a block that contains the residual of the displacement vector term. It is a vector field that is associated with the mapping of the domain from the reference to physical spaces.
};
staticinlineconstexprvectorvector_term{};
usingTerms=utils::types::TypeList<vector>;
staticconstexprvectorvector_term{};///< Static constexpr instance of the vector term. This is used to access the term in the discretization.
usingTerms=utils::types::TypeList<vector>;///< Type list of terms for compile time reflection and iteration over the terms of the field.
};
structsurface_deformationfinal:field{// Surface deformation is a more general field that the others, it is not pointwise defined over the domain. Use concepts to constrain this as a non spatial field.
* @brief The surface corollary field to the full displacement field. This is in some ways more general than the others in so far as it is not pointwise defined over the domain. Rather,
* it is some general field of numbers, divorced from the underlying discrimination, that is used to represent the surface deformation of the system. This may be used, for example
* to represent spherical harmonic coefficients of the surface deformation. For more information see the surface and deformation systems.
*/
structsurface_deformationfinal:field{
staticconstexprstd::string_viewname=meta::type_name<surface_deformation>();///< Compile time reflected name of the field
/**
* @brief The coefficients term represents some set of coefficients associated with whatever surface representation is being used to represent the surface deformation.
* Note how we do not have a functional space, support, or a quantity. This is also where we associate the values with.
staticconstexprstd::string_viewname=meta::type_name<coefficients>();///< Compile time reflected name of the term
structvaluefinal:value_block_base{};///< The actual values of the coefficients for whatever surface representation is being used
};
/**
* @brief The shape equilibrium term represents the shape equilibrium condition of the system. This is a term that is associated with the surface deformation field and is used to enforce the shape equilibrium condition in the weak form.
* This is where we associate the residuals with. Note how we do not have a functional space, support, or a quantity. This is because the shape equilibrium condition is a global condition that is not associated with any particular functional space, support, or quantity.
staticconstexprcoefficientscoefficients_term{};///< Static constexpr instance of the coefficients term. This is used to access the term in the discretization.
staticconstexprshape_equilibriumshape_equilibrium_term{};///< Static constexpr instance of the shape_equilibrium term. This is used to access the term in the discretization.
usingTerms=utils::types::TypeList<coefficients,shape_equilibrium>;///< Type list of terms for compile time reflection and iteration over the terms of the field.
};
/**
* @brief The gravity field is represented as a mixed system, in many ways this field is the motivation for the entire design of this file. Since it is a
* mixed system we have two separate terms that make it up, the potential and the gradient. These are not derived from one and other, rather they are
* weakly enforced to agree. This buys us an additional order of convergence in the potential which in tern allows us to use a lower resolution mesh.
staticconstexprstd::string_viewname=meta::type_name<gravity>();///< Compile time reflected name of the field
/**
* @brief The gravitational gradient term is a 2nd order Hdiv vector field that is used to represent the negative gravitational acceleration of the system.
* Note that when I started developing this code I made the frankly wacky choice to let this be the true gradient of the potential, which is the negative
* acceleration. Broadly this may make sense mathematically; however, it means that developers must be extra careful to remember the sign convention.
usingFunctionalSpace=functional_space::DiscreteFunctionalSpace<functional_space::Hdiv,2,1>;///< 2nd order Hdiv vector field. Read the template parameters as follows: 1) Hdiv space, 2) 2nd order, 3) rank 1 tensor field
usingSupport=domain::AllDomains;///< We support gravity on all domains, primarily since the only real boundary condition we have in the general case is that the potential goes to zero at infinity, which is in the vacuum domain.
usingQuantity=dimensions::Acceleration;///< This is a dimension of acceleration, despite the wacky sign convention I adopted.
staticconstexprstd::string_viewsymbol="∇φ";///< Compile time symbol used for the term. Note that this is a UTF-8 character and may not render correctly in all environments. (TODO: Should we introduce an ASCII fallback?, something like make this a tuple of form <main, fallback>?)
structvaluefinal:value_block_base{};
structresidualfinal:residual_block_base{};
structvaluefinal:value_block_base{};///< The value block is a block that contains the value of the gravitational gradient term. It is a vector field that is associated with the negative gravitational acceleration of the system.
structresidualfinal:residual_block_base{};///< The residual block is a block that contains the residual of the gravitational gradient term. It is a vector field that is associated with the negative gravitational acceleration of the system.
};
/**
* @brief The gravitational potential term is a 2nd order L2 scalar field that is used to represent the gravitational potential of the system. Note that
* this is not exactly the same as the if you were to take the scalar potential of the gravitational gradient term. These are solved for by the code separately and
* weakly enforced to agree. Generally if you need the most accurate gradient you should use the gradient term and if you need the most accurate
* potential you should use this term (this is because differentiating this term loses an order of accuracy in the differentiation).
usingFunctionalSpace=functional_space::DiscreteFunctionalSpace<functional_space::L2,2,0>;///< 2nd order L2 scalar field. Read the template parameters as follows: 1) L2 space, 2) 2nd order, 3) rank 0 tensor field
usingSupport=domain::AllDomains;///< We support gravity on all domains, primarily since the only real boundary condition we have in the general case is that the potential goes to zero at infinity, which is in the vacuum domain.
usingQuantity=dimensions::GravitationalPotential;///< This is a dimension of gravitational potential, which is a scalar field that is associated with the gravitational potential of the system.
staticconstexprstd::string_viewsymbol="φ";///< Compile time symbol used for the term. Note that this is a UTF-8 character and may not render correctly in all environments. (TODO: Should we introduce an ASCII fallback?, something like make this a tuple of form <main, fallback>?)
structvaluefinal:value_block_base{};
structresidualfinal:residual_block_base{};
structvaluefinal:value_block_base{};///< The value block is a block that contains the value of the gravitational potential term. It is a scalar field that is associated with the gravitational potential of the system.
structresidualfinal:residual_block_base{};///< The residual block is a block that contains the residual of the gravitational potential term. It is a scalar field that is associated with the gravitational potential of the system.
};
staticinlineconstexprpotentialpotential_term{};
staticinlineconstexprgradientgradient_term{};
staticconstexprpotentialpotential_term{};///< Static constexpr instance of the potential term. This is used to access the term in the discretization.
staticconstexprgradientgradient_term{};///< Static constexpr instance of the gradient term. This is used to access the term in the discretization.
usingTerms=utils::types::TypeList<potential,gradient>;///< Type list of terms for compile time reflection and iteration over the terms of the field.
};
/**
* @brief The enthalpy field is a scalar field is a C0 continuous scalar field. Note that this field likely will not survive the transition to the more general
* non-barotropic case. In the barotropic case the gradient of enthalpy is parallel to the gradient of the pressure. This make enthalpy a particularly
* useful quantity as we can represent the 0 pressure surface condition as the 0 enthalpy condition and we can break down mass constraint into
usingFunctionalSpace=functional_space::DiscreteFunctionalSpace<functional_space::H1,3,0>;//< 3 vector H1 space (C0 continuous). Read the template parameters as follows: 1) H1 space, 2) 3rd order, 3) rank 0 tensor field
usingSupport=domain::StellarDomains;///< There is only material in the stellar domain therefore we only support the enthalpy field in the stellar domain.
usingQuantity=dimensions::SpecificEnthalpy;///< This has the dimension of specific enthalpy,
staticconstexprstd::string_viewsymbol="h";///< Compile time symbol used for the term.
structvaluefinal:value_block_base{};
structresidualfinal:residual_block_base{};
structvaluefinal:value_block_base{};///< The value block is a block that contains the value of the specific enthalpy term. It is a scalar field that is associated with the specific enthalpy of the system.
structresidualfinal:residual_block_base{};///< The residual block is a block that contains the residual of the specific enthalpy term. It is a scalar field that is associated with the specific enthalpy of the system.
};
staticinlineconstexprspecificspecific_term{};
staticconstexprspecificspecific_term{};///< Static constexpr instance of the specific term. This is used to access the term in the discretization.
usingTerms=utils::types::TypeList<specific>;
usingTerms=utils::types::TypeList<specific>;///< Type list of terms for compile time reflection and iteration over the terms of the field.
* @brief Runtime descriptor for a term, containing its associated field and name. Two TermDescriptors are equal if they have identical fields and names.
*/
structTermDescriptorfinal{
FieldDescriptorfield;
std::string_viewname;
FieldDescriptorfield;///< Each term belongs to a field, and this descriptor identifies that field.
std::string_viewname;///< The name of the term, used for runtime identification.
/**
* @brief Compare two TermDescriptors for equality based on their fields and names.
* @param lhs The left-hand side TermDescriptor.
* @param rhs The right-hand side TermDescriptor.
* @return True if both the fields and names of the TermDescriptors are equal, false otherwise.
* @brief Extract the functional space type associated with a given term type. Will have a type of whatever TermT::FunctionalSpace is.
* @tparam TermT The term type from which to extract the functional space type.
*
* @todo For consistency with other types consider renaming TermT::FunctionalSpace to TermT::functional_space. This would require updating all term definitions to use the new naming convention. CLion or some other IDE should be able to handle this refactor easily.
* @brief Extract the support type associated with a given term type. Will have a type of whatever TermT::Support is.
* @tparam TermT The term type from which to extract the support type.
*
* @todo For consistency with other types consider renaming TermT::Support to TermT::support. This would require updating all term definitions to use the new naming convention. CLion or some other IDE should be able to handle this refactor easily.
* @brief Extract the quantity type associated with a given term type. Will have a type of whatever TermT::Quantity is.
* @tparam TermT The term type from which to extract the quantity type.
*
* @todo For consistency with other types consider renaming TermT::Quantity to TermT::quantity. This would require updating all term definitions to use the new naming convention. CLion or some other IDE should be able to handle this refactor easily.
* @brief Extract the terms associated with a given field type. Will have a type of whatever FieldT::Terms is.
* @tparam FieldT The field type from which to extract the terms.
*
* @todo For consistency with other types consider renaming FieldT::Terms to FieldT::terms. This would require updating all field definitions to use the new naming convention. CLion or some other IDE should be able to handle this refactor easily.
*/
template<typenameFieldT>
usingTermsOf=FieldT::Terms;
/**
* @brief Helper struct representing the traits of some spatial term type, including its functional space, support, family, order, and rank.
* @tparam TermT The spatial term type from which to extract the functional space and support types.
*/
template<SpatialTermTermT>
structSpatialTermTraits{
usingFunctionalSpace=TermT::FunctionalSpace;
usingSupport=TermT::Support;
usingFunctionalSpace=TermT::FunctionalSpace;///< The functional space type associated with the term. e.g. 2nd order scalar H1 space
usingSupport=TermT::Support;///< The support type associated with the term. e.g. stellar domain
staticconstexprautofamily=FunctionalSpace::functional_space.family;///< The family of the functional space associated with the term. e.g. H1, L2, etc.
staticconstexprintorder=FunctionalSpace::order;///< The order of the functional space associated with the term. e.g. 1st order, 2nd order, etc.
staticconstexprstd::size_trank=FunctionalSpace::rank::value;///< The rank of the functional space associated with the term.
};
}
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.