Contents ↓
Chapter 9

From input file to verifiable result

Current MKE-F is a GNU Octave program with a functional numerical core. Model data are created once, solvers return new result structures, and plotting and printing are separate from computation.

Responsibilities

LayerTaskMain code
InputRead sections to EOF, validate records and create elements.FEMesh.m, createStructuralElement.m
AssemblyAccumulate element contributions into sparse global K and M.assembleGlobalMatrices.m
ModelSnapshot of matrices, DOF mapping, supports, loads and element data.createAnalysisModel.m
Numerical coreReduce DOFs, solve and create a new result structure.solveStatic.m, solveModal.m, solveTransient.m
ResultsReactions, equilibrium, local forces and spectrum.calculateStaticEquilibrium.m, recoverElementResults.m, computeSingleSidedSpectrum.m
Compatibility and presentationFamiliar commands RunStatic, RunModal, RunTransient, optional printing and plotting.StructFEProblem.m, plotTransientResult.m

The root function setup explicitly adds directories src/model, src/analysis, src/results and scripts to the Octave path. Supplied models are in examples/cases, ready-to-use postprocessor data are in examples/results, and new results are written to the ignored directory output.

addpath(pwd);
setup;

problem = StructFEProblem(fullfile( ...
    "examples", "cases", "CaseBeam.txt"));
model = problem.GetAnalysisModel();

staticResult = solveStatic(model);
modalResult = solveModal(model);
Running an analysis through the GUI

Start the container interface from the project root with docker compose up --build or docker-compose up --build. In the preprocessor create a model or open a TXT file containing section analysis and click Run analysis. The backend always calls the fixed RunSelected(); the browser never supplies script names, paths or Octave expressions.

Calling a solver does not change model, problem.K or problem.M. Loads are rebuilt for every run, so repeated analysis and changes in analysis order should not change the result.

Elements and sparse assembly

An element is a plain value structure, not a mutable subclass instance. createStructuralElement creates a common contract for types 112 and 113: type, nodeNumbers, properties, a unified vector dofs, matrices and stored length and transformation. The same DOF map is used for assembly and force recovery.

assembleGlobalMatrices first builds row–column–value triplets, then a single call to sparse creates the global matrices. Octave sums contributions at repeated addresses. Memory depends on the number of element coefficients, not the square of the total DOF count.

Architectural invariant

Element mathematics is concentrated in one structure constructor; solvers do not know individual element formulas, and result recovery uses the stored matrices and transformation.

Validation and headless operation

FEMesh checks file opening, section order, field count, finite values, node numbers, element properties, load types and the prohibition on mixed meshes. onCleanup closes the file even on error. Parser messages contain the filename and line; the complete contract is in chapter 10.

By default, verbose=false and plotting=false. Task creation and all three analyses therefore work locally in a terminal without a graphical display. Mesh and history plotting are separate actions; axis equal does not distort mesh geometry.

octave --no-gui --quiet --eval "addpath(pwd); setup; run_octave_tests;"

Verification sequence

  1. run_octave_smoke_tests reads supplied cases, checks sparse assembly and runs available analysis types.
  2. run_verification_tests checks element and global invariants, independent formulas, reactions, loads and call order.
  3. Specialized files check element matrices, assembly, the Mario Paz modal example, Newmark integration, forces and input.
  4. Reference laboratories add educational, executable checks of actual production code.
What is not a reference oracle

The matrices, frequencies and modes of Illustrative Example 11.1 from Mario Paz and William Leigh have been reconstructed and are checked by a dedicated test. Old output snapshots and discrepancies with ANSYS are still not treated as oracles: references are the published example, analytical solutions and explicit invariants.

The 2017 versions and the current program

The author’s articles document project origins but describe the MATLAB architecture of their time. Their class names, breakpoints, matrix mutation order and numerical discrepancies must not be copied into current instructions.

Historical materialWhat it recordsCurrent equivalent
“MKE-F — Newmark method”, v0.4Adoption of Newmark integration and the old MATLAB transient-analysis call.solveTransient, initial conditions, full histories and the verifiable β=1/4, γ=1/2 scheme.
“Mass matrix and stiffness matrix”, v0.6.0Types 112/113, consistent mass and the former hierarchy FiniteElementStructural/Beam2DElement/Truss2DElement.createStructuralElement returns a structure; unified triplet assembly replaces mutable classes and methods Assembler.
“Frame analysis in MKE-F”, v0.6.0 eraOrigin of the case ANSYSBeamStatic01.txt and the historical comparison with Beam188.The file remains a regression input, but the old article’s 5% discrepancy is not a required result.

The “v0.6.0 era” label is chronological: the following day the author explicitly identified the current version as v0.6.0. The frame article itself does not print a version number.

Completed improvement plan map

The following preserves every completed item of the former ToDo.md. Each item names current code and a check; where an intermediate solution was later replaced, its present equivalent is listed.

1. GNU Octave compatibility and local verification

  1. The minimum GNU Octave version 8.4 and local verification on 11.3 are documented in the local workflow chapter; the verification entry point is run_octave_tests.m.
  2. The headless command is implemented in run_octave_smoke_tests.m and run locally by the developer.
  3. All supplied cases assemble, and static, modal, pulse and harmonic analyses run in run_octave_smoke_tests.m; matrix invariants are repeated in tests/run_verification_tests.m.
  4. The intermediate concrete fallback methods of the old abstract class were later replaced by structures in createStructuralElement.m; the interface is checked by tests/test_element_matrices.m.
  5. Testing arrays of homogeneous handle-class elements became irrelevant after migration to structures; their array is created by FEMesh.m and checked by the smoke test.
  6. Optional printing and plotting are implemented in StructFEProblem.m; defaults are checked by tests/test_input_validation.m.
  7. UTF-8 is specified in .editorconfig; reading Russian files is exercised by all smoke tests.
  8. Octave-supported operations and repeated solves are concentrated in solveTransient.m and validateReducedSystem.m; these are checked by tests/test_newmark_transient.m.
  9. The small local runner is run_octave_smoke_tests.m; successful completion is the check itself.
  10. The single-argument constructor is preserved in StructFEProblem.m, and headless settings are checked in tests/test_input_validation.m.

2. Self-contained verification suite

  1. The Octave-only checks are in tests/run_verification_tests.m and included through run_octave_tests.m.
  2. Dimensions, finiteness and symmetry of K/M are checked by testMatrixInvariants in the same file and tests/test_element_matrices.m.
  3. Formulas FL/(EA) and PL³/(3EI) are checked by testSingleAxialTruss and testCantileverBeam.
  4. Reactions, mass, single-step pulse and harmonic samples are checked by tests/run_verification_tests.m.
  5. All supplied files serve as no-crash regression checks in run_octave_smoke_tests.m.
  6. Explicit absolute and relative tolerances are implemented by helper functions assertClose and assertRelativeSmall in the tests.
  7. The complete local suite runs through run_octave_tests.m.
  8. The Mario Paz example is checked against published matrices, frequencies and modes in tests/test_mario_paz_modal.m; the ANSYS example remains historical context only.

3. Element matrices

  1. The symmetric coefficient -22*L of the frame consistent mass matrix is in createStructuralElement.m; symmetry is checked by tests/test_element_matrices.m.
  2. Coefficients EA/L, 12EI/L³, 6EI/L² are written explicitly there and compared with an independent matrix in the same test.
  3. Zero length and nonpositive A, E, ρ, I are rejected in createStructuralElement.m; testInvalidElementData checks the errors.
  4. Horizontal, vertical and inclined orientations are checked by functions testTrussOrientations and testBeamOrientations.
  5. Symmetry, rank, rigid-body modes, rotational invariance and mass are checked in tests/test_element_matrices.m.

4. Functional core

  1. The model separates data from results in createAnalysisModel.m; testFunctionalAnalysisCore checks immutability.
  2. Pure entry points are solveStatic.m, solveModal.m, solveTransient.m; the facade delegates to them in StructFEProblem.m.
  3. K and M are not modified: this is explicitly checked by testFunctionalAnalysisCore.
  4. Fresh loads for each run are created by buildStaticLoad.m and buildTransientLoad.m; the same test compares repeated calls.
  5. Two identical runs are compared in testFunctionalAnalysisCore.
  6. Independence from static, modal and dynamic call order is checked there as well.
  7. Named fields are populated by the three solve*.m; the same test checks key fields.
  8. Plotting is separated into plotTransientResult.m, while printing stays in the facade; headless operation is checked by the smoke test and tests/test_input_validation.m.

5. Free-DOF reduction

  1. fixedDOFs and freeDOFs constructs partitionDOFs.m; the mapping is checked by testFreeDOFReduction.
  2. Reduced K and M are used by all three solvers; corresponding slices are checked in tests/run_verification_tests.m.
  3. Spurious zero modes are eliminated in solveModal.m; positive frequencies are checked by testFreeDOFReduction.
  4. A load on a constrained DOF is retained in solveStatic.m; its reaction is checked by the same test.
  5. Global equilibrium is calculated by calculateStaticEquilibrium.m; bar/cantilever tests check zero residuals.
  6. Repeated, invalid and insufficient constraints are rejected by partitionDOFs.m and validateReducedSystem.m; errors are checked by testConstraintValidation.

6. Nodal load semantics

  1. Indexed DOF assembly is performed by getNodalLoadComponents.m together with the load builders; multiple records are checked by testNodalLoadSemantics.
  2. The third frame component is treated as Mz, and rejected for a truss; the code and test are the same.
  3. Historical type 10 produces one rectangular step in buildTransientLoad.m; the exact sample is checked by testNodalLoadSemantics.
  4. Explicit bcforce_pulse/type 12 is read by FEMesh.m; input file CaseExplicitPulseBeam.txt is checked in the load test.
  5. bcforce_step/type 13 is implemented by the same files; input file CaseStepBeam.txt checks a constant history.
  6. Summation of multiple loads at the same and different nodes is checked by testNodalLoadSemantics.

7. Newmark transient analysis

  1. Average acceleration with β=1/4, γ=1/2 is implemented in solveTransient.m; parameters are checked by tests/test_newmark_transient.m.
  2. The grid with first column t=0 is implemented there and checked in testFreeVibrationAndInitialState.
  3. Initial u, v and equilibrium-based acceleration are implemented there; the same test and invalid-input scenarios check them.
  4. Histories of size stepCount+1 are preallocated in solveTransient.m; the transient-analysis test checks their size.
  5. The constant effective matrix is factorized once there; the full transient test suite checks solution consistency.
  6. FFT and single-sided normalization are performed by computeSingleSidedSpectrum.m; the peak is checked by testSingleSidedSpectrum.
  7. Histories and spectra are returned independently of graphics; transient tests check the fields, and plotting is separated into plotTransientResult.m.
  8. Analytical SDOF response, energy and convergence are checked in testFreeVibrationAndInitialState, testImpulseResponseAndConvergence and testBeamImpulseRefinement.

8. Result recovery

  1. The compatibility name StressCalc.m delegates to the new implementation recoverElementResults.m; the interface is checked by tests/test_result_recovery.m.
  2. Truss strain, stress and axial force are calculated by recoverElementResults.m; straight and rotated members are checked there.
  3. The same function returns local frame end forces; diagrams were initially deferred until distributed loads were available, and the cantilever is checked by testCantileverEndForces.
  4. Single-member and cantilever references are in tests/test_result_recovery.m.

9. Simpler, faster assembly

  1. Structures and pure functions are implemented in createStructuralElement.m; matrix and result-recovery tests check the interface.
  2. The class value-semantics item was resolved by removing the mutable hierarchy; the absence of handle-class elements follows from the structures FEMesh.allMeshElems and is checked by the smoke test.
  3. The unified field interface is used by assembleGlobalMatrices.m and recoverElementResults.m; both paths have dedicated tests.
  4. A single element.dofs creates createStructuralElement.m; mapping and summation are checked by tests/test_sparse_assembly.m.
  5. Triplet sparse matrices are created by assembleGlobalMatrices.m; testSuppliedCasesAreSparse and testLargeTrussChain check the result.
  6. Length and transformation are cached in the element structure; matrix and recovery tests use and verify these data.
  7. The full verification suite and smoke tests ensure baseline results are preserved.

10. Input, documentation and presentation

  1. The read-to-EOF loop is in FEMesh.m; repeated sections and unknown markers are checked by tests/test_input_validation.m.
  2. fopen, onCleanup and line numbers are implemented there; the same test checks opening errors and invalid records.
  3. Repeated supports and loads are appended in FEMesh.m; CaseRepeatedSections.txt checks the count and sum.
  4. Node numbers, connectivity, property counts and plotted DOF selection are checked by FEMesh.m/StructFEProblem.m; test input files and testPlotSelectionAndDefaults check the errors.
  5. Mixed types are rejected as MKEF:UnsupportedMixedMesh; CaseMixedElements.txt checks the error line.
  6. Optional graphics and printing are in StructFEProblem.m, and axis equal in FEMesh.m; the input test checks default values.
  7. Units, DOFs, loads, limitations, sources and commands are preserved in this reference, especially in chapter 10; laboratories and the full test suite provide executable confirmation.

Analysis diagnostics

SymptomLikely causeWhat to check
MKEF:SingularStiffnessA mechanism, disconnected node or insufficient supports.Connectivity, element lengths, fixedDOFs; do not “fix” a mechanism by adding ones to K.
MKEF:SingularMass or an invalid modal problemNonpositive or singular reduced mass.ρ, A, I, free DOFs and connectivity.
Unexpected reaction or force signGlobal and local systems, or forces on the structure and on an element, have been confused.element.transformation, the order of localEndForces and chapter 8.
Unexpected dynamic impulseType 10 was mistaken for a constant step.Use type 12 for an explicit pulse and type 13 for a step; see chapter 4.
Error with filename and line numberA section format rule was violated.Compare the record with chapter 10; retain the line number in any error report.

Chapter source code

Project history

  • Daniil Denisov, “MKE-F — Newmark method,” November 25, 2017, version 0.4.
  • Daniil Denisov, “Mass matrix and stiffness matrix,” December 20, 2017, version 0.6.0.
  • Daniil Denisov, “Frame analysis in MKE-F,” December 19, 2017, v0.6.0 era.