Robin boundary conditions

Robin boundary condition is a special type of boundary condition that may be interpreted as a Neumann boundary condition, but where the flux depends on the field-variable. For scalar problems, we can write the Robin BC contribution as

\[\int_{\Gamma_\mathrm{R}} \delta u\ q_\mathrm{n}(u)\ \mathrm{d}\Gamma\]

where $u$ is the primary variable, $\Gamma_\mathrm{R}$ is the Robin boundary, and $q_\mathrm{n}(u)$ is the boundary flux that depends on the solution $u$. In this "how-to", we implement a simple linear Robin boundary condition, $q_\mathrm{n}(u) = k[u - u_\mathrm{b}]$. This can be interpreted physically for e.g. a thermal problem as a resistance, $1/k$, of heat transfer from the domain to the outside, where the latter has a constant temperature $u_\mathrm{b}$.

Implementing Robin BC

To implement this, we will overload the facet_routine! function, which allows us to calculate both a residual and stiffness contribution from facets. Hence, we first define a "material", RobinBC, and implement the facet_routine! that encodes the contribution to the weak form as outlined above.

using Ferrite, FerriteAssembly

struct RobinBC{T}
    k::T
    ub::T
end

function FerriteAssembly.facet_routine!(
        Ke, re, ae, rbc::RobinBC, fv::AbstractFacetValues, facetbuffer
        )
    for q_point in 1:getnquadpoints(fv)
        dΓ = getdetJdV(fv, q_point)
        u = function_value(fv, q_point, ae)
        qn = rbc.k*(u - rbc.ub) # Flux out of domain
        for i in 1:getnbasefunctions(fv)
            δN = shape_value(fv, q_point, i)
            re[i] += δN*qn*dΓ
            for j in 1:getnbasefunctions(fv)
                N = shape_value(fv, q_point, j)
                Ke[i,j] += δN*rbc.k*N*dΓ
            end
        end
    end
end;

Standard Ferrite.jl setup

To assemble the contributions from the Robin boundary, we need the regular setup for a Ferrite simulation, e.g.

grid = generate_grid(Quadrilateral, (10,10))
ip = Lagrange{RefQuadrilateral,1}()

dh = DofHandler(grid)
add!(dh, :u, ip)
close!(dh)

K = allocate_matrix(dh)
r = zeros(ndofs(dh))
a = zeros(ndofs(dh));

Using Robin BC

For the Robin boundary condition, we also need to define the integration via FacetValues, in addition to an instance of RobinBC, for which we set the "outside temperature" to -1.0.

facet_qr = FacetQuadratureRule{RefQuadrilateral}(2);
fv = FacetValues(facet_qr, ip);
rbc = RobinBC(1.0, -1.0);

Finally, we set up a domain buffer with the actual materials and facetset, and create an assembler. Naturally, the boundary conditions are combined with other contributions, such as the assembly over cells. Typically, the same assembler can be used. If not, it is important to set the keyword arguments such that K and r are not zeroed between different calls to work!.

domainbuffer = setup_domainbuffer(DomainSpec(dh, rbc, fv; set=getfacetset(grid, "right")))
assembler = start_assemble(K, r)
work!(assembler, domainbuffer; a=a);

Visualizing the BC

To visualize the output, we can export the vtk the residual, showing how contributions have been added to the boundary.

VTKGridFile("RobinBC", grid) do vtk
    write_solution(vtk, dh, r)
end;

This page was generated using Literate.jl.