From 85da6b7ee03525f41974e67d80fc590ea44cd514 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Mon, 9 Nov 2026 13:56:07 -0500 Subject: [PATCH] refactor(web): deleted demo library web directory --- web/demo.js | 195 ------------------ web/index.html | 116 ----------- web/meson.build | 34 ---- web/smoke.cjs | 26 --- web/styles.css | 506 ---------------------------------------------- web/wasm_demo.cpp | 173 ---------------- 6 files changed, 1050 deletions(-) delete mode 100644 web/demo.js delete mode 100644 web/index.html delete mode 100644 web/meson.build delete mode 100644 web/smoke.cjs delete mode 100644 web/styles.css delete mode 100644 web/wasm_demo.cpp diff --git a/web/demo.js b/web/demo.js deleted file mode 100644 index 1236a8e8..00000000 --- a/web/demo.js +++ /dev/null @@ -1,195 +0,0 @@ -"use strict"; - -const elements = { - form: document.querySelector("#solve-form"), - cells: document.querySelector("#cells"), - cellsValue: document.querySelector("#cells-value"), - order: document.querySelector("#order"), - button: document.querySelector("#solve-button"), - status: document.querySelector("#runtime-status"), - statusDot: document.querySelector("#status-dot"), - capabilities: document.querySelector("#capability-list"), - canvas: document.querySelector("#mesh-canvas"), - runIndex: document.querySelector("#run-index"), - dofs: document.querySelector("#metric-dofs"), - iterations: document.querySelector("#metric-iterations"), - error: document.querySelector("#metric-error"), - time: document.querySelector("#metric-time"), - log: document.querySelector("#runtime-log"), - footerVersion: document.querySelector("#footer-version"), -}; - -let mfemModule; -let completedRuns = 0; -let lastCells = Number(elements.cells.value); - -function setStatus(message, state = "loading") { - elements.status.textContent = message; - elements.statusDot.dataset.state = state; -} - -function appendLog(message) { - const timestamp = new Date().toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - second: "2-digit", - }); - const prior = elements.log.textContent.startsWith("Waiting") ? "" : `${elements.log.textContent}\n`; - elements.log.textContent = `${prior}[${timestamp}] ${message}`; -} - -function drawMesh(cells, solved = false) { - const canvas = elements.canvas; - const context = canvas.getContext("2d"); - const ratio = Math.max(1, window.devicePixelRatio || 1); - const bounds = canvas.getBoundingClientRect(); - const width = Math.max(320, Math.round(bounds.width)); - const height = Math.max(260, Math.round(bounds.height)); - canvas.width = width * ratio; - canvas.height = height * ratio; - context.scale(ratio, ratio); - - const gradient = context.createRadialGradient( - width * 0.5, - height * 0.46, - 10, - width * 0.5, - height * 0.5, - width * 0.66, - ); - gradient.addColorStop(0, solved ? "rgba(125, 249, 197, 0.30)" : "rgba(89, 199, 255, 0.16)"); - gradient.addColorStop(0.55, solved ? "rgba(35, 124, 126, 0.20)" : "rgba(27, 72, 104, 0.13)"); - gradient.addColorStop(1, "rgba(8, 14, 24, 0)"); - context.fillStyle = gradient; - context.fillRect(0, 0, width, height); - - const padding = Math.max(30, Math.min(width, height) * 0.11); - const side = Math.min(width - 2 * padding, height - 2 * padding); - const left = (width - side) / 2; - const top = (height - side) / 2; - const step = side / cells; - - context.strokeStyle = solved ? "rgba(151, 255, 215, 0.30)" : "rgba(138, 207, 236, 0.22)"; - context.lineWidth = 1; - context.beginPath(); - for (let index = 0; index <= cells; index += 1) { - const offset = Math.round(index * step) + 0.5; - context.moveTo(left + offset, top); - context.lineTo(left + offset, top + side); - context.moveTo(left, top + offset); - context.lineTo(left + side, top + offset); - } - context.stroke(); - - context.strokeStyle = solved ? "#7df9c5" : "#59c7ff"; - context.lineWidth = 2; - context.strokeRect(left, top, side, side); -} - -function renderCapabilities(capabilities) { - const entries = [ - ["Wasm", capabilities.wasm], - ["MFEM", true], - ["libCEED", capabilities.ceed], - ["zlib", capabilities.zlib], - ["MPI", capabilities.mpi], - ]; - elements.capabilities.replaceChildren( - ...entries.map(([name, enabled]) => { - const badge = document.createElement("span"); - badge.className = enabled ? "capability enabled" : "capability disabled"; - badge.textContent = enabled ? name : `${name} off`; - return badge; - }), - ); - elements.footerVersion.textContent = `MFEM ${capabilities.mfemVersion} · WebAssembly`; -} - -function formatScientific(value) { - return Number(value).toExponential(3); -} - -function formatTime(seconds) { - const milliseconds = Number(seconds) * 1000; - return milliseconds < 1000 ? `${milliseconds.toFixed(1)} ms` : `${Number(seconds).toFixed(2)} s`; -} - -async function runSolve() { - const cells = Number(elements.cells.value); - const order = Number(elements.order.value); - elements.button.disabled = true; - elements.button.classList.add("working"); - setStatus("Solving finite-element system", "working"); - appendLog(`Starting ${cells}×${cells} mesh at polynomial order ${order}.`); - - await new Promise((resolve) => window.setTimeout(resolve, 20)); - try { - const raw = mfemModule.ccall( - "mfem_demo_solve", - "string", - ["number", "number"], - [cells, order], - ); - const result = JSON.parse(raw); - if (!result.ok) { - throw new Error(result.error || "The MFEM solve failed."); - } - - completedRuns += 1; - lastCells = cells; - elements.runIndex.textContent = `Run ${String(completedRuns).padStart(2, "0")}`; - elements.dofs.textContent = Number(result.trueDofs).toLocaleString(); - elements.iterations.textContent = Number(result.iterations).toLocaleString(); - elements.error.textContent = formatScientific(result.l2Error); - elements.time.textContent = formatTime(result.elapsedSeconds); - drawMesh(cells, true); - setStatus("Solve complete", "ready"); - appendLog( - `Converged in ${result.iterations} iterations with residual ${formatScientific(result.finalNorm)}.`, - ); - } catch (error) { - setStatus("Solve failed", "error"); - appendLog(error instanceof Error ? error.message : String(error)); - } finally { - elements.button.disabled = false; - elements.button.classList.remove("working"); - } -} - -elements.cells.addEventListener("input", () => { - elements.cellsValue.value = elements.cells.value; - drawMesh(Number(elements.cells.value), false); -}); - -elements.form.addEventListener("submit", (event) => { - event.preventDefault(); - void runSolve(); -}); - -window.addEventListener("resize", () => drawMesh(lastCells, completedRuns > 0)); - -async function initialize() { - drawMesh(lastCells, false); - try { - if (typeof MFEMDemoModule !== "function") { - throw new Error("The generated MFEM WebAssembly loader was not found."); - } - mfemModule = await MFEMDemoModule({ - locateFile: (path) => new URL(path, window.location.href).href, - print: (message) => appendLog(message), - printErr: (message) => appendLog(`Runtime: ${message}`), - }); - const capabilities = JSON.parse( - mfemModule.ccall("mfem_demo_capabilities", "string", [], []), - ); - renderCapabilities(capabilities); - elements.button.disabled = false; - setStatus("Runtime ready", "ready"); - appendLog(`Loaded MFEM ${capabilities.mfemVersion} from WebAssembly.`); - } catch (error) { - setStatus("Runtime failed to load", "error"); - appendLog(error instanceof Error ? error.message : String(error)); - } -} - -void initialize(); diff --git a/web/index.html b/web/index.html deleted file mode 100644 index c4369950..00000000 --- a/web/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - MFEM WebAssembly Lab - - - - - -
- - - - MFEM - WebAssembly Lab - - -
- - Loading numerical runtime -
-
- -
-
-
Poisson equation · unit square
-

Solve a finite-element system in your browser.

-

- MFEM assembles and solves the system locally through WebAssembly. - Change the mesh or polynomial order and compare the resulting error and workload. -

- -
-
-
- - 10 -
- - -
- -
- - -
- - -
- -
- Runtime -
-
-
- -
-
-
-
Computed result
-

Finite-element mesh

-
- Awaiting run -
- -
- -
- −Δu = f - u = 0 on ∂Ω -
-
- -
-
- True DOFs - — -
-
- CG iterations - — -
-
- L² error - — -
-
- Browser time - — -
-
- -
- Runtime details -
Waiting for WebAssembly initialization…
-
-
-
- - - - diff --git a/web/meson.build b/web/meson.build deleted file mode 100644 index e178d44a..00000000 --- a/web/meson.build +++ /dev/null @@ -1,34 +0,0 @@ -wasm_browser_demo = [] - -if is_wasm and get_option('build_examples') - configure_file(input: 'index.html', output: 'index.html', copy: true) - configure_file(input: 'styles.css', output: 'styles.css', copy: true) - configure_file(input: 'demo.js', output: 'demo.js', copy: true) - - wasm_browser_demo = executable( - 'mfem-wasm-demo', - 'wasm_demo.cpp', - dependencies: [mfem_dep, template_include_dep], - override_options: ['cpp_std=' + mfem_consumer_cpp_std], - link_args: [ - '-sMODULARIZE=1', - '-sEXPORT_NAME=MFEMDemoModule', - '-sEXPORTED_FUNCTIONS=_main,_mfem_demo_solve,_mfem_demo_capabilities', - '-sEXPORTED_RUNTIME_METHODS=ccall', - '-sNO_EXIT_RUNTIME=1', - '-sENVIRONMENT=web,node', - ], - install: false, - ) - - if get_option('build_tests') - node_program = find_program('node', required: true) - test( - 'wasm-browser-api', - node_program, - args: [files('smoke.cjs'), wasm_browser_demo], - depends: wasm_browser_demo, - timeout: 120, - ) - endif -endif diff --git a/web/smoke.cjs b/web/smoke.cjs deleted file mode 100644 index 24672c8b..00000000 --- a/web/smoke.cjs +++ /dev/null @@ -1,26 +0,0 @@ -"use strict"; - -const path = require("node:path"); - -async function main() { - const modulePath = path.resolve(process.argv[2]); - const createModule = require(modulePath); - const module = await createModule({ print() {}, printErr() {} }); - const capabilities = JSON.parse( - module.ccall("mfem_demo_capabilities", "string", [], []), - ); - const result = JSON.parse( - module.ccall("mfem_demo_solve", "string", ["number", "number"], [6, 2]), - ); - if (!capabilities.wasm || !result.ok || result.trueDofs <= 0 || result.iterations <= 0) { - throw new Error(`Unexpected MFEM Wasm result: ${JSON.stringify(result)}`); - } - process.stdout.write( - `MFEM ${capabilities.mfemVersion} Wasm solve: dofs=${result.trueDofs} iterations=${result.iterations} l2=${result.l2Error}\n`, - ); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); diff --git a/web/styles.css b/web/styles.css deleted file mode 100644 index cfb429a7..00000000 --- a/web/styles.css +++ /dev/null @@ -1,506 +0,0 @@ -:root { - color-scheme: dark; - --bg: #071018; - --surface: #0c1823; - --surface-raised: #102230; - --line: rgba(165, 214, 232, 0.16); - --line-strong: rgba(165, 214, 232, 0.29); - --text: #eef8fb; - --muted: #91aab5; - --cyan: #59c7ff; - --mint: #7df9c5; - --amber: #f5c86c; - --danger: #ff8f8f; - --radius: 18px; - font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -} - -* { - box-sizing: border-box; -} - -body { - min-width: 320px; - min-height: 100vh; - margin: 0; - color: var(--text); - background: - radial-gradient(circle at 18% -10%, rgba(48, 139, 173, 0.20), transparent 34rem), - linear-gradient(150deg, #071018 0%, #09131d 48%, #060d14 100%); -} - -button, -input, -select { - font: inherit; -} - -.masthead, -footer { - width: min(1420px, calc(100% - 48px)); - margin: 0 auto; -} - -.masthead { - min-height: 92px; - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid var(--line); -} - -.brand { - display: inline-flex; - gap: 12px; - align-items: center; - color: inherit; - text-decoration: none; -} - -.brand-mark { - width: 42px; - height: 42px; - display: grid; - place-items: center; - color: #061019; - background: var(--mint); - border-radius: 11px; - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-weight: 900; - box-shadow: 0 0 30px rgba(125, 249, 197, 0.20); -} - -.brand strong, -.brand small { - display: block; -} - -.brand strong { - letter-spacing: 0.16em; - font-size: 0.88rem; -} - -.brand small { - margin-top: 2px; - color: var(--muted); - font-size: 0.76rem; -} - -.runtime-state { - display: flex; - align-items: center; - gap: 9px; - color: var(--muted); - font-size: 0.82rem; -} - -.status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--amber); - box-shadow: 0 0 14px currentColor; -} - -.status-dot[data-state="ready"] { - background: var(--mint); -} - -.status-dot[data-state="working"] { - background: var(--cyan); - animation: pulse 1s ease-in-out infinite; -} - -.status-dot[data-state="error"] { - background: var(--danger); -} - -.workspace { - width: min(1420px, calc(100% - 48px)); - min-height: calc(100vh - 168px); - margin: 0 auto; - padding: 54px 0 44px; - display: grid; - grid-template-columns: minmax(300px, 0.72fr) minmax(560px, 1.28fr); - gap: clamp(32px, 6vw, 92px); - align-items: start; -} - -.control-panel { - padding-top: 12px; -} - -.eyebrow { - margin-bottom: 14px; - color: var(--mint); - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.72rem; - letter-spacing: 0.14em; - text-transform: uppercase; -} - -h1, -h2, -p { - margin-top: 0; -} - -h1 { - max-width: 12ch; - margin-bottom: 20px; - font-size: clamp(2.5rem, 5vw, 4.8rem); - font-weight: 560; - letter-spacing: -0.055em; - line-height: 0.99; -} - -h2 { - margin-bottom: 0; - font-size: clamp(1.4rem, 2vw, 2rem); - font-weight: 560; - letter-spacing: -0.035em; -} - -.lede { - max-width: 56ch; - margin-bottom: 34px; - color: var(--muted); - font-size: 1rem; - line-height: 1.7; -} - -form { - display: grid; - grid-template-columns: 1fr 0.72fr; - gap: 18px; -} - -.field-group { - display: flex; - flex-direction: column; - gap: 11px; -} - -.field-heading { - display: flex; - justify-content: space-between; - align-items: baseline; -} - -label, -.field-heading output { - font-size: 0.78rem; - font-weight: 650; - letter-spacing: 0.035em; -} - -.field-heading output { - color: var(--mint); - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; -} - -input[type="range"] { - width: 100%; - accent-color: var(--mint); -} - -.range-labels { - display: flex; - justify-content: space-between; - margin-top: -7px; - color: #607b87; - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.64rem; -} - -select { - min-height: 46px; - padding: 0 42px 0 14px; - color: var(--text); - background: var(--surface-raised); - border: 1px solid var(--line-strong); - border-radius: 10px; -} - -button { - grid-column: 1 / -1; - min-height: 54px; - margin-top: 5px; - padding: 0 18px; - display: flex; - align-items: center; - justify-content: space-between; - color: #061019; - background: var(--mint); - border: 0; - border-radius: 11px; - cursor: pointer; - font-weight: 750; - transition: transform 160ms ease, box-shadow 160ms ease, opacity 160ms ease; -} - -button:hover:not(:disabled) { - transform: translateY(-2px); - box-shadow: 0 12px 32px rgba(125, 249, 197, 0.18); -} - -button:focus-visible, -select:focus-visible, -input:focus-visible, -summary:focus-visible { - outline: 2px solid var(--cyan); - outline-offset: 3px; -} - -button:disabled { - cursor: wait; - opacity: 0.46; -} - -.capabilities { - margin-top: 24px; - display: flex; - align-items: center; - gap: 12px; - flex-wrap: wrap; -} - -.capability-label { - color: var(--muted); - font-size: 0.7rem; - text-transform: uppercase; - letter-spacing: 0.12em; -} - -#capability-list { - display: flex; - gap: 7px; - flex-wrap: wrap; -} - -.capability { - padding: 5px 8px; - border: 1px solid var(--line); - border-radius: 999px; - color: var(--muted); - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.65rem; -} - -.capability.enabled { - color: var(--mint); - border-color: rgba(125, 249, 197, 0.27); - background: rgba(125, 249, 197, 0.06); -} - -.result-panel { - overflow: hidden; - background: rgba(12, 24, 35, 0.84); - border: 1px solid var(--line); - border-radius: var(--radius); - box-shadow: 0 32px 90px rgba(0, 0, 0, 0.25); - backdrop-filter: blur(14px); -} - -.result-heading { - padding: 24px 26px 21px; - display: flex; - align-items: flex-end; - justify-content: space-between; - border-bottom: 1px solid var(--line); -} - -.result-heading .eyebrow { - margin-bottom: 7px; -} - -.run-index { - color: var(--muted); - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.72rem; -} - -.mesh-frame { - position: relative; - min-height: 410px; - background: - linear-gradient(rgba(255, 255, 255, 0.012) 1px, transparent 1px), - linear-gradient(90deg, rgba(255, 255, 255, 0.012) 1px, transparent 1px), - #09131c; - background-size: 24px 24px; -} - -#mesh-canvas { - width: 100%; - height: 410px; - display: block; -} - -.equation { - position: absolute; - top: 18px; - right: 18px; - padding: 10px 12px; - color: var(--text); - background: rgba(7, 16, 24, 0.78); - border: 1px solid var(--line); - border-radius: 9px; - font-family: ui-serif, Georgia, serif; - text-align: center; -} - -.equation span, -.equation small { - display: block; -} - -.equation small { - margin-top: 3px; - color: var(--muted); - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.57rem; -} - -.metrics { - display: grid; - grid-template-columns: repeat(4, 1fr); - border-top: 1px solid var(--line); - border-bottom: 1px solid var(--line); -} - -.metrics article { - min-width: 0; - padding: 18px; - border-right: 1px solid var(--line); -} - -.metrics article:last-child { - border-right: 0; -} - -.metrics span, -.metrics strong { - display: block; -} - -.metrics span { - margin-bottom: 8px; - color: var(--muted); - font-size: 0.68rem; - letter-spacing: 0.055em; - text-transform: uppercase; -} - -.metrics strong { - overflow: hidden; - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: clamp(0.92rem, 1.4vw, 1.15rem); - text-overflow: ellipsis; -} - -.console-panel { - padding: 16px 24px 20px; -} - -.console-panel summary { - color: var(--muted); - cursor: pointer; - font-size: 0.75rem; -} - -.console-panel pre { - max-height: 150px; - overflow: auto; - margin: 14px 0 0; - color: #a9c7d2; - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - font-size: 0.68rem; - line-height: 1.6; - white-space: pre-wrap; -} - -footer { - min-height: 76px; - display: flex; - align-items: center; - justify-content: space-between; - color: #67808b; - border-top: 1px solid var(--line); - font-size: 0.7rem; -} - -@keyframes pulse { - 0%, 100% { opacity: 0.55; transform: scale(0.9); } - 50% { opacity: 1; transform: scale(1.2); } -} - -@media (max-width: 960px) { - .workspace { - grid-template-columns: 1fr; - gap: 42px; - } - - h1 { - max-width: 15ch; - } -} - -@media (max-width: 620px) { - .masthead, - .workspace, - footer { - width: min(100% - 28px, 1420px); - } - - .masthead { - min-height: 78px; - } - - .runtime-state { - max-width: 145px; - justify-content: flex-end; - text-align: right; - } - - .workspace { - padding-top: 32px; - } - - h1 { - font-size: clamp(2.35rem, 13vw, 3.4rem); - } - - form, - .metrics { - grid-template-columns: 1fr 1fr; - } - - .metrics article:nth-child(2) { - border-right: 0; - } - - .metrics article:nth-child(-n + 2) { - border-bottom: 1px solid var(--line); - } - - .mesh-frame, - #mesh-canvas { - min-height: 320px; - height: 320px; - } - - footer { - gap: 18px; - } -} - -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - scroll-behavior: auto !important; - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} diff --git a/web/wasm_demo.cpp b/web/wasm_demo.cpp deleted file mode 100644 index 0faecd3b..00000000 --- a/web/wasm_demo.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace -{ -constexpr double pi = 3.141592653589793238462643383279502884; - -double exact_solution(const mfem::Vector &point) -{ - return std::sin(pi * point[0]) * std::sin(pi * point[1]); -} - -double forcing_function(const mfem::Vector &point) -{ - return 2.0 * pi * pi * exact_solution(point); -} - -std::string json_escape(const std::string &input) -{ - std::ostringstream output; - for (const unsigned char character : input) - { - switch (character) - { - case '"': output << "\\\""; break; - case '\\': output << "\\\\"; break; - case '\b': output << "\\b"; break; - case '\f': output << "\\f"; break; - case '\n': output << "\\n"; break; - case '\r': output << "\\r"; break; - case '\t': output << "\\t"; break; - default: - if (character < 0x20) - { - output << "\\u" << std::hex << std::setw(4) - << std::setfill('0') << static_cast(character) - << std::dec; - } - else - { - output << character; - } - } - } - return output.str(); -} - -const char *error_response(const std::string &message) -{ - static std::string response; - response = "{\"ok\":false,\"error\":\"" + json_escape(message) + "\"}"; - return response.c_str(); -} -} // namespace - -extern "C" EMSCRIPTEN_KEEPALIVE const char *mfem_demo_capabilities() -{ - static const std::string capabilities = []() { - std::ostringstream output; - output << "{\"mfemVersion\":\"" << json_escape(MFEM_VERSION_STRING) - << "\",\"mpi\":" << (MESON_MFEM_HAS_MPI ? "true" : "false") - << ",\"hypre\":" << (MESON_MFEM_HAS_HYPRE ? "true" : "false") - << ",\"zlib\":" << (MESON_MFEM_HAS_ZLIB ? "true" : "false") - << ",\"ceed\":" << (MESON_MFEM_HAS_CEED ? "true" : "false") - << ",\"fms\":" << (MESON_MFEM_HAS_FMS ? "true" : "false") - << ",\"wasm\":true}"; - return output.str(); - }(); - return capabilities.c_str(); -} - -extern "C" EMSCRIPTEN_KEEPALIVE const char *mfem_demo_solve(int cells, int order) -{ - static std::string response; - try - { - if (cells < 2 || cells > 64) - { - throw std::invalid_argument("cells must be between 2 and 64"); - } - if (order < 1 || order > 4) - { - throw std::invalid_argument("polynomial order must be between 1 and 4"); - } - - static mfem::Device device("cpu"); - (void)device; - mfem::StopWatch timer; - timer.Start(); - - mfem::Mesh mesh = mfem::Mesh::MakeCartesian2D( - cells, cells, mfem::Element::QUADRILATERAL, true, 1.0, 1.0); - mfem::H1_FECollection elements(order, mesh.Dimension()); - mfem::FiniteElementSpace space(&mesh, &elements); - - mfem::Array essential_boundary(mesh.bdr_attributes.Max()); - essential_boundary = 1; - mfem::Array essential_dofs; - space.GetEssentialTrueDofs(essential_boundary, essential_dofs); - - mfem::FunctionCoefficient exact(exact_solution); - mfem::FunctionCoefficient forcing(forcing_function); - mfem::LinearForm rhs(&space); - rhs.AddDomainIntegrator(new mfem::DomainLFIntegrator(forcing)); - rhs.Assemble(); - - mfem::GridFunction solution(&space); - solution = 0.0; - solution.ProjectBdrCoefficient(exact, essential_boundary); - - mfem::BilinearForm diffusion(&space); - mfem::ConstantCoefficient one(1.0); - diffusion.AddDomainIntegrator(new mfem::DiffusionIntegrator(one)); - diffusion.Assemble(); - - mfem::OperatorPtr matrix; - mfem::Vector linear_rhs; - mfem::Vector linear_solution; - diffusion.FormLinearSystem( - essential_dofs, solution, rhs, matrix, linear_solution, linear_rhs); - - mfem::GSSmoother smoother(static_cast(*matrix)); - mfem::CGSolver solver; - solver.SetOperator(*matrix); - solver.SetPreconditioner(smoother); - solver.SetRelTol(1e-10); - solver.SetAbsTol(0.0); - solver.SetMaxIter(400); - solver.SetPrintLevel(0); - solver.Mult(linear_rhs, linear_solution); - diffusion.RecoverFEMSolution(linear_solution, rhs, solution); - - timer.Stop(); - const double l2_error = solution.ComputeL2Error(exact); - if (!solver.GetConverged() || !std::isfinite(l2_error)) - { - throw std::runtime_error("MFEM conjugate-gradient solve did not converge"); - } - - std::ostringstream output; - output << std::setprecision(10) - << "{\"ok\":true" - << ",\"mfemVersion\":\"" << json_escape(MFEM_VERSION_STRING) << '"' - << ",\"cells\":" << cells - << ",\"elements\":" << mesh.GetNE() - << ",\"order\":" << order - << ",\"trueDofs\":" << space.GetTrueVSize() - << ",\"iterations\":" << solver.GetNumIterations() - << ",\"finalNorm\":" << solver.GetFinalNorm() - << ",\"l2Error\":" << l2_error - << ",\"elapsedSeconds\":" << timer.RealTime() - << '}'; - response = output.str(); - return response.c_str(); - } - catch (const std::exception &error) - { - return error_response(error.what()); - } -} - -int main() -{ - return 0; -}