Sat. Dec 6th, 2025

Implementation

using Gridap
using Random
using Printf
using LinearAlgebra

function run_ch()
    kappa        = 1e-3
    dt           = 1e-4
    n_steps      = 500
    plot_interval = 1

    domain    = (0.0, 1.0, 0.0, 1.0)
    partition = (2^8, 2^8)
    model = CartesianDiscreteModel(domain, partition)

    order = 1
    reffe = ReferenceFE(lagrangian, Float64, order)

    V = TestFESpace(model, reffe; conformity = :H1)
    U = TrialFESpace(V)

    Y = MultiFieldFESpace([V, V])  # test : (v, q)
    X = MultiFieldFESpace([U, U])  # trial: (c, μ)

    Ω  = Triangulation(model)
    deg = 2 * order
    dΩ = Measure(Ω, deg)

    # ============================
    # 初期条件
    # ============================
    Random.seed!(0)
    N = num_free_dofs(U)

    mean_c0 = 0.0
    c_vec = mean_c0 .+ 0.01 .* randn(N)
    c_vec .-= mean(c_vec) .- mean_c0

    mkpath("ch_output")
    vtufiles = String[]
    times = Float64[]

    c0_h = FEFunction(U, c_vec)
    writevtk(Ω, "ch_output/ch_0000", cellfields = ["c" => c0_h])
    push!(vtufiles, "ch_0000.vtu")
    push!(times, 0.0)

    a((c, μ), (v, q)) = ∫(
          c * v
        + dt * (∇(μ) ⋅ ∇(v))
        - kappa * (∇(c) ⋅ ∇(q))
        + μ * q
    ) * dΩ
    A = assemble_matrix(a, X, Y)
    factA = lu(A)

    # ============================
    # Time Evolution Loop
    # ============================
    for step in 1:n_steps

        c_old_h = FEFunction(U, c_vec)

        l((v, q)) = ∫(
              c_old_h * v
            + (c_old_h * c_old_h * c_old_h - c_old_h) * q
        ) * dΩ

        b = assemble_vector(l, Y)
        x_vec = factA \ b
        xh = FEFunction(X, x_vec)

        c_h, μ_h = xh

        c_vec = get_free_dof_values(c_h)

        if (step % plot_interval == 0) || (step == n_steps)
            fname = @sprintf("ch_output/ch_%04d", step)
            writevtk(Ω, fname, cellfields = ["c" => c_h])
            push!(vtufiles, @sprintf("ch_%04d.vtu", step))
            push!(times, step * dt)
            @info "t-step = $step : wrote $fname.vtu"
        end
    end
    write_pvd_collection("ch_output/ch_series.pvd", vtufiles, times)
end

function write_pvd_collection(path, files, times)
    open(path, "w") do io
        println(io, """<?xml version="1.0"?>""")
        println(io, """<VTKFile type="Collection" version="0.1" byte_order="LittleEndian">""")
        println(io, "  <Collection>")
        for (f, t) in zip(files, times)
            println(io, @sprintf("""    <DataSet timestep="%.6f" group="" part="0" file="%s"/>""", t, f))
        end
        println(io, "  </Collection>")
        println(io, "</VTKFile>")
    end
end

run_ch()