Date: 2026-04-30
Goal: modernize the package internals, improve solver performance, and establish a robust testing/performance baseline while keeping the current user interface intact (or only very slightly changed with compatibility shims).
- Coordinate/geometry dispatch is encoded by floating-point tags in
MeshStructure.dimension(examples:1,1.5,2,2.5,2.8,3,3.2). - This numeric dispatch pattern is repeated in many files (
meshstructure,domainVariables,boundarycondition,diffusionterms,convectionTerms,calculusTerms,sourceTerms,transientTerms, utilities), increasing maintenance cost and bug risk. - Core solve API (
solveLinearPDE) is hardcoded toSparseMatrixCSC{Float64, Int64}and uses directM\RHSonly. - Time-stepping workflows rebuild many sparse structures in loops and allocate repeatedly.
- Type stability is limited by fields like
dimension::Real,dims::Array{Int,1}, and value unions such asUnion{Array{<:Real}, DenseArray{Bool}}. - Existing
test/runtests.jlis a smoke test only; there is no robust regression/performance test suite. src/jfvm_test.jlbehaves like a demonstration script and does not enforce pass/fail correctness.
- Keep all currently exported function names and high-level workflows working.
- Keep all mesh constructor names (such as
createMesh1D,createMeshCylindrical2D, etc.). - Allow minor, controlled breakage only for internals and undocumented behavior.
- Any user-visible change must ship with:
- deprecation warning (not immediate removal),
- migration note,
- regression tests proving old and new behavior equivalence.
Introduce explicit geometry/coordinate types:
abstract type AbstractCoordinateSystem end
struct Cartesian1D <: AbstractCoordinateSystem end
struct Cylindrical1D <: AbstractCoordinateSystem end
struct Cartesian2D <: AbstractCoordinateSystem end
struct Cylindrical2D <: AbstractCoordinateSystem end
struct Radial2D <: AbstractCoordinateSystem end
struct Cartesian3D <: AbstractCoordinateSystem end
struct Cylindrical3D <: AbstractCoordinateSystem endUpdate mesh type to carry coordinate semantics directly:
struct MeshStructure{CS<:AbstractCoordinateSystem}
coordinatesystem::CS
dims::NTuple{3,Int} # canonical storage, unused axes set to 1
# existing geometry fields ...
endNotes:
- Internal canonical dims should be fixed-size for type stability.
- Public constructors still accept 1D/2D/3D style arguments.
- Keep convenience helpers for active dimensions (
ndims_active(mesh)), and geometry family checks (is_cartesian(mesh),is_cylindrical(mesh),is_radial(mesh)).
- Keep
dimensionavailable for one transition release as a computed compatibility property or mirrored field with deprecation warning. - Maintain mapping:
Cartesian1D -> 1.0Cylindrical1D -> 1.5Cartesian2D -> 2.0Cylindrical2D -> 2.5Radial2D -> 2.8Cartesian3D -> 3.0Cylindrical3D -> 3.2
- Remove direct numeric comparisons from internal kernels incrementally and replace with dispatch/functions.
- Introduce internal dispatch entry points first, for example:
diffusionTerm(::FaceValue, ::Cartesian2D)diffusionTerm(::FaceValue, ::Cylindrical3D)
- Temporary adapter:
diffusionTerm(D::FaceValue) = diffusionTerm(D, D.domain.coordinatesystem)
- Repeat for convection, divergence/gradient, source, transient, boundary, and averaging.
- Migrate one subsystem at a time, preserving behavior by regression tests before moving to next subsystem.
- Change broad abstract fields to concrete parameterized forms where feasible.
- Replace
dims::Array{Int,1}with tuple-backed representation internally. - Rework value containers toward parameterized arrays:
CellValue{T,N,A<:AbstractArray{T,N}}FaceValue{T,AX,AY,AZ}(or equivalent typed fields)
- Keep constructor front-end permissive (accept scalars, arrays, booleans), normalize internally.
- Add
@viewsand in-place broadcasting in hot paths. - Eliminate avoidable
zeros(...)/repeat(...)in term assembly. - Cache frequently reused geometry vectors (cell widths, face widths, radial factors) per mesh.
- Introduce reusable sparse assembly workspaces for repeated transient solves.
Add internal solver strategy object while preserving current API:
solveLinearPDE(mesh, A, b) # existing behavior preserved
solveLinearPDE(mesh, A, b; solver=DefaultLU())
solveLinearPDE!(mesh, A, b, phi; solver=...)Recommended initial solver backends:
- Direct: built-in sparse
\(default). - Optional direct: MUMPS if available.
- Optional iterative: GMRES/BiCGStab/CG via
IterativeSolvers.jl(for large systems).
- Add optional reusable factorization path for repeated solves with fixed matrix pattern.
- In transient loops, separate matrix build and solve phases clearly.
- Add cache invalidation rules when coefficients change.
- Relax strict
Float64typing in solver signatures where safe. - Preserve default Float64 path for stability and performance.
- Replace single smoke test with modules in
test/:- mesh constructors and geometry invariants,
- boundary condition assembly,
- diffusion/convection/transient term assembly,
- solver outputs for canonical PDE cases,
- coordinate-system equivalence tests.
- Use small deterministic cases for each coordinate family.
- Validate:
- matrix sparsity pattern,
- residual norms,
- solution norms/profiles,
- mass/flux consistency checks.
- Confirm old user-facing calls still run unchanged.
- Add deprecation tests for any intentionally adjusted behavior.
- Add benchmark harness for representative problems (1D/2D/3D).
- Track:
- assembly time,
- solve time,
- memory allocations,
- iteration counts (iterative solvers).
- Build robust tests from current behavior.
- Add benchmark scenarios and save baseline metrics.
- Fix obvious syntax/logic defects discovered by tests.
Exit criteria:
- tests pass consistently,
- benchmark baseline recorded,
- no API changes yet.
- Add coordinate structs and constructor wiring.
- Add compatibility mapping for old numeric dimensions.
- Migrate one low-risk subsystem first (for example source/transient terms).
Exit criteria:
- old and new pathways produce numerically equivalent outputs,
- no public constructor breakage.
- Migrate diffusion, convection, boundary, calculus, averaging, utilities.
- Remove direct numeric comparisons from internals.
- Keep compatibility shim active.
Exit criteria:
- no internal
d==1.5-style logic in core kernels, - full test suite passes,
- no measurable regression in baseline benchmarks.
- Add solver abstraction and optional iterative backends.
- Implement factorization reuse and key in-place optimizations.
- Benchmark and tune high-allocation hotspots.
Exit criteria:
- default solver behavior unchanged for existing users,
- improved performance on medium/large cases,
- documented solver options.
- Keep compatibility warnings, prepare migration notes.
- Decide whether to retain or remove numeric
dimensioncompatibility in next major version.
Exit criteria:
- clear migration guide,
- release notes with any slight breaks documented.
- Risk: hidden behavior differences across coordinate systems during migration.
- Mitigation: matrix-level and solution-level regression tests per geometry.
- Risk: performance regressions from type refactor.
- Mitigation: benchmark gate in CI, maintain fast path for Float64.
- Risk: optional solver dependencies increase maintenance burden.
- Mitigation: keep optional backends behind feature flags and test matrix.
- Risk: over-refactor before tests are reliable.
- Mitigation: enforce test-first sequence (Phase A before B/C/D).
This section is intentionally broader and exploratory.
- 2D polygonal and 3D polyhedral control volumes.
- Face-based connectivity with owner/neighbor topology.
- Native support for non-orthogonal corrections.
- Boundary patch groups with named tags.
- Introduce
AbstractMeshwith two concrete families:StructuredMesh(current style),UnstructuredMesh(new).
- Introduce geometry operator layer using mesh connectivity rather than index slicing.
- Separate discretization operators from storage layout.
- Mesh import from Gmsh/VTK formats.
- Potential interoperability with Julia mesh ecosystems for preprocessing.
- Data structure choice for sparse connectivity and cache efficiency.
- Non-orthogonal correction strategy and limiter design.
- Boundary condition API that works uniformly across structured/unstructured meshes.
- Picard (fixed-point) framework for mild nonlinearities.
- Newton or quasi-Newton framework for stronger nonlinear systems.
- Optional line search or trust-region globalisation.
- Residual/Jacobian monitoring and convergence diagnostics.
- Add equation/problem object abstraction with callbacks:
- residual assembly,
- Jacobian assembly or Jacobian-free operator,
- preconditioner assembly hooks.
- Keep existing linear workflows as a subset of the new formulation.
- Automatic differentiation support for Jacobians.
- Matrix-free Krylov methods for large coupled systems.
- Block preconditioners for multiphysics extensions.
- Built-in export to VTK/XDMF for external post-processing.
- Lightweight plotting recipes for 1D/2D in Julia plotting ecosystem.
- Optional interactive visualization backend for notebooks.
- Consistent plotting API for structured and future unstructured meshes.
- Field metadata (units, labels, variable names) carried with outputs.
- Standardized snapshot and animation utilities for transient runs.
- Documentation overhaul:
- modern tutorials,
- migration guide,
- solver/mesh capability matrix.
- CI modernization:
- multi-version Julia tests,
- benchmark regression checks,
- optional dependency matrix.
- Numerical quality:
- conservation and boundedness test suite,
- verification cases against analytical or manufactured solutions.
- Developer experience:
- clearer module organization,
- contribution guide,
- coding standards for performance-critical kernels.
- Wave 1: finish Part I (stabilize/refactor/performance).
- Wave 2: prototype unstructured mesh core + minimal diffusion solver.
- Wave 3: nonlinear framework over structured meshes first, then unstructured.
- Wave 4: unified visualization/output pipeline across all mesh types.
Before committing to full implementation, define:
- target problem scales (small academic vs large industrial),
- preferred dependency strategy (minimal dependencies vs richer ecosystem),
- maintenance capacity for optional backends,
- performance targets for structured and unstructured modes.
- Start with Part I Phase A: create a real test suite from existing behavior.
- Establish baseline benchmarks for at least one case in each geometry family.
- Open a refactor branch focused only on coordinate-type introduction with compatibility shims.