Skip to content
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
1 change: 1 addition & 0 deletions .github/workflows/Documenter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: Documentation
on:
push:
branches: [master]
tags: ['*']
paths-ignore:
- 'CONTRIBUTING.md'
- 'CODE_OF_CONDUCT.md'
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.20.1] - 2026-03-19

### Changed

- Minor changesto incorporate the changes in Gridap 0.20, specifically the pullback machinery, into GridapDistributed. Since PR[#1258](https://github.com/gridap/Gridap.jl/pull/1258).

## [0.20.0] - 2026-03-18

This is a major release with breaking changes, mainly in the `Polynomials` and `ReferenceFEs` modules. The main goal of this release is to provide a more flexible and extensible framework for polynomial bases and reference finite elements based on moments, as a stepping stone for the implementation of more complicated elements (e.g. MTW, etc...).
Expand Down
29 changes: 19 additions & 10 deletions src/FESpaces/CLagrangianFESpaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,24 +85,33 @@ function CLagrangianFESpace(
space
end

function _use_clagrangian(trian,cell_reffe,conf)
false
# Allowed configurations of CLagrangianFESpace constructors
# We need:
# - H1 conformity
# - Both the geometric and FE reffes have to be the same (and linear - this can be relaxed in the future)
# - nvertices == nnodes (no periodicity, etc...)
# - No constraints (this can be relaxed in the future)

_use_clagrangian(args...) = false

function _use_clagrangian(geo_reffe::GenericLagrangianRefFE, fe_reffe::GenericLagrangianRefFE)
return (get_orders(geo_reffe) == get_orders(fe_reffe)) && get_order(geo_reffe) == 1
end

function _use_clagrangian(trian::Triangulation,cell_reffe,conf::H1Conformity)
function _use_clagrangian(trian::Triangulation, cell_reffe::AbstractArray{<:ReferenceFE}, ::H1Conformity)
ctype_reffe1, cell_ctype1 = compress_cell_data(cell_reffe)
ctype_reffe2 = get_reffes(trian)
if length(ctype_reffe1) != 1 || length(ctype_reffe2) != 1
return false
end
reffe1 = first(ctype_reffe1)
reffe2 = first(ctype_reffe2)
!(reffe1 isa GenericLagrangianRefFE && reffe2 isa GenericLagrangianRefFE) && return false
get_orders(reffe1) != get_orders(reffe2) && return false
# This can be relaxed in the future
get_order(reffe1) != 1 && return false
return _use_clagrangian(reffe1,reffe2)
end

true
function _use_clagrangian(trian::Triangulation, reffe, conf::H1Conformity)
cell_reffe = ReferenceFE(trian, reffe)
return _use_clagrangian(trian,cell_reffe,conf)
end

function _unsafe_clagrangian(
Expand All @@ -119,8 +128,8 @@ function _unsafe_clagrangian(
T = value_type(prebasis)
# Next line assumes linear grids
node_to_tag = get_face_tag_index(labels,dirichlet_tags,0)
_vector_type = vector_type === nothing ? Vector{Float64} : vector_type
tag_to_mask = dirichlet_masks === nothing ? fill(_default_mask(T),length(dirichlet_tags)) : dirichlet_masks
_vector_type = isnothing(vector_type) ? Vector{Float64} : vector_type
tag_to_mask = isnothing(dirichlet_masks) ? fill(_default_mask(T),length(dirichlet_tags)) : dirichlet_masks
CLagrangianFESpace(T,grid,_vector_type,node_to_tag,tag_to_mask,trian)
end

Expand Down
49 changes: 31 additions & 18 deletions src/FESpaces/ConformingFESpaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,14 @@ end
Minimum data required to build a conforming FE space.
At this moment, the some cell-wise info is compressed on cell types.
This can be relaxed in the future, and have an arbitrary cell-wise data.

# Constructors

CellFE(model::DiscreteModel, cell_reffe::AbstractArray{<:ReferenceFE}, conformity::Conformity; kwargs...)

"""
struct CellFE <: GridapType
conformity::Conformity
cell_conformity::CellConformity
cell_shapefuns::AbstractArray{<:AbstractVector{<:Field}}
cell_dof_basis::AbstractArray{<:AbstractVector{<:Dof}}
Expand All @@ -199,38 +205,45 @@ end
# reasonable quadrature rule to integrate the shape functions. Only used by FESpace
# constructors that need to integrate the shape functions (e.g., ZeroMeanFESpace).

Conformity(cell_fe::CellFE) = cell_fe.conformity
CellConformity(cell_fe::CellFE) = cell_fe.cell_conformity
Geometry.num_cells(cell_fe::CellFE) = num_cells(CellConformity(cell_fe))
Geometry.get_cell_type(cell_fe::CellFE) = get_cell_type(CellConformity(cell_fe))

"""
CellFE(model::DiscreteModel,cell_reffe::AbstractArray{<:ReferenceFE},conformity::Conformity, args...)

Generate a CellFE from a vector of reference fes
"""
# This constructor allows for provided shapefuns and dofs, which
# is necessary for GridapDistributed
function CellFE(
model::DiscreteModel,
cell_reffe::AbstractArray{<:ReferenceFE},
conformity::Conformity;
kwargs...
)
model::DiscreteModel, cell_reffe::AbstractArray{<:ReferenceFE},
cell_shapefuns::AbstractArray, cell_dof_basis::AbstractArray,
conformity::Conformity
)
cell_conformity = CellConformity(cell_reffe,conformity)
ctype_reffe, _ = compress_cell_data(cell_reffe)
shapefuns_and_dofs = get_cell_shapefuns_and_dof_basis(model,cell_reffe,conformity;kwargs...)
cell_shapefuns, cell_dof_basis = shapefuns_and_dofs
cell_shapefuns_domain = ReferenceDomain()
cell_dof_basis_domain = ReferenceDomain()
max_order = maximum(map(get_order,ctype_reffe))
CellFE(
cell_conformity,
cell_shapefuns,
cell_dof_basis,
cell_shapefuns_domain,
cell_dof_basis_domain,
max_order
conformity, cell_conformity, cell_shapefuns, cell_dof_basis,
cell_shapefuns_domain, cell_dof_basis_domain, max_order
)
end

function CellFE(
model::DiscreteModel, cell_reffe::AbstractArray{<:ReferenceFE}, conformity::Conformity; kwargs...
)
cell_shapefuns, cell_dof_basis = get_cell_shapefuns_and_dof_basis(
model, cell_reffe, conformity; kwargs...
)
return CellFE(model, cell_reffe, cell_shapefuns, cell_dof_basis, conformity)
end

function CellFE(
model::DiscreteModel, cell_reffe::AbstractArray{<:ReferenceFE}, conformity; kwargs...
)
conf = Conformity(testitem(cell_reffe),conformity)
return CellFE(model,cell_reffe,conf;kwargs...)
end

"""
get_cell_shapefuns_and_dof_basis(
model::DiscreteModel, cell_reffe::AbstractArray, conf::Conformity; kwargs...)
Expand Down
169 changes: 76 additions & 93 deletions src/FESpaces/FESpaceFactories.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@

function FESpace(t::Triangulation,reffes;trian=nothing, kwargs...)
@assert trian === nothing || trian === t "The `trian` keyword must be consistant with the given tiangulation $t."
"""
"""
function TestFESpace(args...;kwargs...)
FESpace(args...;kwargs...)
end

function FESpace(t::Triangulation, reffes; trian=nothing, kwargs...)
@assert isnothing(trian) || trian === t "The `trian` keyword must be consistant with the given tiangulation $t."
model = get_active_model(t)
FESpace(model, reffes; trian=t, kwargs...)
end

function FESpace(
model::DiscreteModel,
reffe::Union{ReferenceFE,Tuple{<:Union{ReferenceFEName,Symbol},Any,Any}};
kwargs...
)
cell_reffe = ReferenceFE(model,reffe)
FESpace(model,cell_reffe;kwargs...)
end

"""
FESpace(model::DiscreteModel, reffe; kwargs...)
FESpace(trian::Triangulation, reffe; kwargs...)
Expand Down Expand Up @@ -33,128 +48,96 @@ is safer, because the polytope(s) is(are) then chosen consistently with `model`

- `trian=Triangulation(model)`.

# Extended help

Internal constructors:
# Internal constructors:

FESpace(model::DiscreteModel, cell_reffe::AbstractArray{<:ReferenceFE}; kwargs...)

FESpace(model::DiscreteModel, cell_fe::CellFE;
trian, labels, dirichlet_tags, dirichlet_masks, constraint, vector_type)
FESpace(model::DiscreteModel, cell_fe::CellFE; kwargs...)
"""
function FESpace(
model::DiscreteModel,
cell_fe::CellFE;
cell_reffe::AbstractArray{<:ReferenceFE};
conformity=nothing,
trian = Triangulation(model),
labels = get_face_labeling(model),
dirichlet_tags=Int[],
dirichlet_masks=nothing,
constraint=nothing,
vector_type=nothing)
vector_type=nothing,
scale_dof=false,
global_meshsize=nothing
)
conf = Conformity(testitem(cell_reffe),conformity)
reg_grid = (num_nodes(model) == num_vertices(model))
if _use_clagrangian(trian,cell_reffe,conf) && isnothing(constraint) && reg_grid
return _unsafe_clagrangian(
cell_reffe,
Triangulation(model),
labels,
vector_type,
dirichlet_tags,
dirichlet_masks,
trian)
end
cell_fe = CellFE(model, cell_reffe, conf; scale_dof, global_meshsize)
return FESpace(
model,cell_fe; trian, labels, dirichlet_tags, dirichlet_masks, constraint, vector_type
)
end

function FESpace(
model::DiscreteModel,
cell_fe::CellFE;
trian = Triangulation(model),
labels = get_face_labeling(model),
dirichlet_tags=Int[],
dirichlet_masks=nothing,
constraint=nothing,
vector_type=nothing
)
@assert num_cells(cell_fe) == num_cells(model) """\n
The number of cells provided in the `cell_fe` argument ($(cell_fe.num_cells) cells)
does not match the number of cells ($(num_cells(model)) cells) in the provided DiscreteModel.
"""
conformity = Conformity(cell_fe)
_vector_type = _get_vector_type(vector_type,cell_fe,trian)
F = _ConformingFESpace(
_vector_type,
model,
labels,
cell_fe,
dirichlet_tags,
dirichlet_masks,
trian)
if isa(conformity,L2Conformity) && isempty(dirichlet_tags)
F = _DiscontinuousFESpace(_vector_type,trian,cell_fe)
else
F = _ConformingFESpace(
_vector_type, model, labels, cell_fe,
dirichlet_tags, dirichlet_masks, trian
)
end
V = _add_constraint(F,cell_fe.max_order,constraint)
V
return V
end

# Private methods

function _get_vector_type(vector_type,cell_fe,trian)
if vector_type == nothing
if isnothing(vector_type)
cell_shapefuns, cell_dof_basis = compute_cell_space(cell_fe,trian)
T = get_dof_value_type(cell_shapefuns,cell_dof_basis)
_vector_type = Vector{T}
return Vector{T}
else
@assert vector_type <: AbstractVector """\n
The (optional) argument vector_type has to be <: AbstractVector.
"""
_vector_type = vector_type
return vector_type
end
_vector_type
end

function _add_constraint(F,order,constraint)
if constraint == nothing
V = F
function _add_constraint(space,order,constraint)
if isnothing(constraint)
return space
elseif constraint == :zeromean
trian = get_triangulation(F)
trian = get_triangulation(space)
dΩ = Measure(trian,order)
V = ZeroMeanFESpace(F,dΩ)
else
@unreachable """\n
The passed option constraint=$constraint is not valid.
Valid values for constraint: nothing, :zeromean
"""
end
V
end

function FESpace(
model::DiscreteModel,
cell_reffe::AbstractArray{<:ReferenceFE};
conformity=nothing,
trian = Triangulation(model),
labels = get_face_labeling(model),
dirichlet_tags=Int[],
dirichlet_masks=nothing,
constraint=nothing,
vector_type=nothing,
scale_dof=false,
global_meshsize=nothing)

conf = Conformity(testitem(cell_reffe),conformity)

if _use_clagrangian(trian,cell_reffe,conf) &&
constraint === nothing &&
num_vertices(model) == num_nodes(model)

V = _unsafe_clagrangian(
cell_reffe,
Triangulation(model),
labels,
vector_type,
dirichlet_tags,
dirichlet_masks,
trian)
return V
end

cell_fe = CellFE(model, cell_reffe, conf; scale_dof, global_meshsize)
vector_type = _get_vector_type(vector_type,cell_fe,trian)
if conformity in (L2Conformity(),:L2) && dirichlet_tags == Int[]
F = _DiscontinuousFESpace(vector_type,trian,cell_fe)
V = _add_constraint(F,cell_fe.max_order,constraint)
else
V = FESpace(model,cell_fe; trian, labels, dirichlet_tags, dirichlet_masks, constraint, vector_type)
return ZeroMeanFESpace(space,dΩ)
end
return V
end

function FESpace(model::DiscreteModel,
reffe::Tuple{<:Union{ReferenceFEName,Symbol},Any,Any}; kwargs...)
reffe_name, reffe_args,reffe_kwargs = reffe
cell_reffe = ReferenceFE(model,reffe_name,reffe_args...;reffe_kwargs...)
FESpace(model,cell_reffe;kwargs...)
end

function FESpace(model::DiscreteModel,
reffe::ReferenceFE; kwargs...)
cell_reffe = Fill(reffe,num_cells(model))
FESpace(model,cell_reffe;kwargs...)
end

"""
"""
function TestFESpace(args...;kwargs...)
FESpace(args...;kwargs...)
@unreachable """\n
The passed option constraint=$constraint is not valid.
Valid values for constraint: nothing, :zeromean
"""
end
2 changes: 1 addition & 1 deletion src/FESpaces/PhysicalFEs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function FiniteElements(
max_order = maximum(map(get_order,ctype_reffe))

return CellFE(
cell_conformity, cell_shapefuns, cell_dof_basis,
conf, cell_conformity, cell_shapefuns, cell_dof_basis,
cell_shapefuns_domain, cell_dof_basis_domain, max_order
)
end
Loading
Loading