fix: add abaqus uel wrapper contract

This commit is contained in:
김경종
2026-06-12 09:08:25 +09:00
parent 44d4ebadf8
commit d455e0843e
16 changed files with 309 additions and 48 deletions
+2 -2
View File
@@ -12,7 +12,7 @@
- Phase directory: `phases/uel-3d-euler-beam`
- Current planned entry point: `python scripts/execute.py uel-3d-euler-beam`
- First pending action: correct Abaqus-facing `UEL` wrapper/interface mismatch before `step8` validation readiness
- First pending action: execute `step8` validation readiness using the corrected Abaqus-facing `UEL` wrapper/interface
## Planned Steps
@@ -46,4 +46,4 @@
- Confirm exact first-scope beam assumptions in the requirements step.
- Decide `PROPS`/`JPROPS` ordering and orientation-vector convention in the interface step.
- Decide exact no-Abaqus Fortran source and test file layout in the reference model step.
- Add a top-level Abaqus-callable `SUBROUTINE UEL(...)` wrapper and correct the `VARIABLES`/`NSVARS` policy before external Abaqus validation readiness.
- External Abaqus validation artifacts still need to be supplied by the user before solver-result comparison.
+16 -6
View File
@@ -5,8 +5,8 @@
- Active objective: 3D Euler-Bernoulli beam Abaqus/Standard `UEL`
- Active phase: `phases/uel-3d-euler-beam`
- Active owner: unassigned
- Current status: completed ad hoc UEL ABI research correction note after step 7 implementation review
- Next action: correct the Abaqus-facing UEL wrapper/interface mismatch before executing `phases/uel-3d-euler-beam/step8.md` validation-readiness step
- Current status: completed Abaqus-facing UEL wrapper/interface correction after step 7 implementation review
- Next action: execute `phases/uel-3d-euler-beam/step8.md` validation-readiness step with corrected `VARIABLES=1` / `NSVARS>=1` contract
## Completed
@@ -54,6 +54,11 @@
- Confirmed current source has no top-level Abaqus-callable `SUBROUTINE UEL(...)` wrapper under `src/fortran`.
- Confirmed `uel3deb_abi_static` is a no-Abaqus adapter, not the manual Abaqus UEL ABI.
- Identified a likely Abaqus input contract issue: documented `*USER ELEMENT, VARIABLES` must be greater than zero, so the current `NSVARS=0` / `VARIABLES=0` contract should be corrected before external Abaqus validation.
- Completed Abaqus-facing UEL ABI correction.
- Added `src/fortran/uel_3d_euler_beam_uel.for` with top-level fixed-form `SUBROUTINE UEL(...)`, `ABA_PARAM.INC`, manual Abaqus array dimensions, adapter delegation, and fatal `XIT` path.
- Updated `uel3deb_abi_static` to accept `NSVARS>=1`, use explicit/manual-style array bounds, and validate `LFLAGS(1)` and `LFLAGS(4)`.
- Updated no-Abaqus tests and wrapper source-smoke tests for the corrected contract.
- Updated `docs/io-definitions/uel-3d-euler-beam.md`, `docs/reference-models/uel-3d-euler-beam.md`, and `docs/corrections/uel-3d-euler-beam-uel-abi-correction.md`.
## In Progress
@@ -65,17 +70,21 @@
## Last Verification
Latest verification after creating the UEL ABI research document:
Latest verification after correcting the Abaqus-facing UEL wrapper/interface:
```bash
python -m unittest discover -s scripts -p "test_*.py"
python scripts/validate_reference_artifacts.py
python scripts/validate_fortran.py
python scripts/validate_workspace.py
git diff --check
```
Result: all passed.
- `python -m unittest discover -s scripts -p "test_*.py"`: 57 tests passed.
- `python scripts/validate_workspace.py`: reference artifact validation succeeded; six Fortran manifest executables compiled and passed with Intel `ifx`; workspace validation succeeded.
- `python -m unittest discover -s scripts -p "test_*.py"`: 60 tests passed.
- `python scripts/validate_reference_artifacts.py`: reference artifact metadata validation succeeded.
- `python scripts/validate_fortran.py`: six Fortran manifest executables compiled and passed with Intel `ifx`.
- `python scripts/validate_workspace.py`: reference validation and Fortran validation succeeded.
- `git diff --check`: passed with line-ending warnings only.
Previous verification after completing step 7 Fortran implementation:
@@ -98,5 +107,6 @@ Result: all passed.
- Read `AGENTS.md`, `PLAN.md`, `PROGRESS.md`, and `WORKNOTE.md`.
- Confirm no other owner is active in this file.
- Correct the Abaqus-facing `UEL` wrapper/interface mismatch before `phases/uel-3d-euler-beam/step8.md`.
- Proceed to `phases/uel-3d-euler-beam/step8.md`.
- Use `VARIABLES=1` in external Abaqus `*USER ELEMENT` examples; the adapter now requires `NSVARS>=1`.
- Update this file when step status changes or before handing off.
+2
View File
@@ -17,3 +17,5 @@
- 같은 조사에서 현재 `src/fortran`에는 Abaqus가 직접 호출할 top-level external `SUBROUTINE UEL(...)` wrapper가 없고, `uel3deb_abi_static`은 no-Abaqus adapter일 뿐임을 확인했다. 다음 구현 보정에서는 manual signature와 `ABA_PARAM.INC` include를 보존하는 wrapper가 필요하다.
- `docs/io-definitions/uel-3d-euler-beam.md``VARIABLES=0`/`NSVARS=0` 정책은 Abaqus `*USER ELEMENT` 문서의 `VARIABLES` 값이 0보다 커야 한다는 규칙과 충돌할 가능성이 있다. 실제 Abaqus-facing 계약은 `VARIABLES=1` 이상 또는 target-version 근거가 있는 예외로 보정해야 한다.
- PowerShell에서 `git add ... && git status --short`를 실행하면 이 환경에서는 `&&`가 statement separator로 인식되지 않아 실패한다. 명령은 분리해서 실행한다.
- 2026-06-12 UEL ABI correction에서 TDD guard는 production file basename token을 `tests/` 아래 테스트 파일명에서 찾는다. `scripts/test_uel_3d_euler_beam_uel.py`만으로는 `src/fortran/uel_3d_euler_beam_uel.for` 변경이 차단되어, `tests/test_uel_3d_euler_beam_uel.py` shim을 함께 추가했다.
- `tests/fortran/uel_3d_euler_beam/test_invalid_inputs.f90`의 helper 인자 순서를 바꿀 때 `NDLOAD` 케이스가 `lflag3=0`으로 들어가 `E016`보다 `E015`가 먼저 발생했다. negative test는 목표 조건 외의 request flag를 모두 유효값으로 유지해야 한다.
+5 -1
View File
@@ -3,10 +3,14 @@
## Metadata
- feature_id: abaqus-uel-subroutines
- related_feature: `uel-3d-euler-beam`
- status: ready-for-interface-correction
- status: implemented-follow-up
- owner_agent: research-agent
- date: 2026-06-12
## Follow-up Status
The findings below captured the pre-correction state after the initial step 7 implementation. The follow-up correction in `docs/corrections/uel-3d-euler-beam-uel-abi-correction.md` adds the missing Abaqus-callable wrapper, changes the Abaqus-facing state-variable policy to `NSVARS>=1` / `VARIABLES=1`, and adds `LFLAGS(1)` and `LFLAGS(4)` validation.
## Research Questions
- Abaqus/Standard가 실제로 호출하는 `UEL` subroutine의 positional ABI는 무엇인가?
- `RHS`, `AMATRX`, `SVARS`, `ENERGY`, `PNEWDT`, `LFLAGS`의 update 책임은 무엇인가?
@@ -10,6 +10,8 @@
## Implementation Scope
Post-step ABI correction note: the original step 7 implementation deliberately deferred the fixed-form Abaqus `UEL` wrapper. The follow-up correction report `docs/corrections/uel-3d-euler-beam-uel-abi-correction.md` supersedes that deferred-wrapper status and records the added wrapper, `NSVARS>=1` policy, and `LFLAGS(1)/(4)` checks.
Production source added in this step:
- `src/fortran/uel_3d_euler_beam_kernel.f90`
@@ -0,0 +1,78 @@
# 3D Euler-Bernoulli Beam UEL ABI Correction Report
## Metadata
- feature_id: uel-3d-euler-beam
- source_failure_report: `docs/abaqus-uel-subroutines-research.md`
- source_implementation_report: `docs/build-test-reports/uel-3d-euler-beam-green.md`
- source_implementation_plan: N/A; correction follows the post-review research findings
- status: corrected-for-build-test
- owner_agent: correction-agent
- date: 2026-06-12
## Failure Triage
- classification: upstream-contract plus implementation defect
- first_failed_command: `python -m unittest scripts.test_uel_3d_euler_beam_uel`; `python scripts/validate_fortran.py`
- failed_target_or_test: missing Abaqus `UEL` wrapper; adapter rejected `NSVARS=1`
- evidence_tail: wrapper source-smoke failed because `src/fortran/uel_3d_euler_beam_uel.for` was missing; Fortran ABI test failed with `actual=6 expected=0` for a valid `NSVARS=1` static call
- triage_decision: implementation-owned correction allowed after research clarified the Abaqus manual ABI
## Root Cause Summary
- root_cause_type: implementation boundary defect and stale interface policy
- summary: the step 7 code implemented a testable kernel and no-Abaqus adapter, but did not expose the top-level Abaqus `SUBROUTINE UEL(...)`. The adapter also kept the pre-research `NSVARS=0` policy, conflicting with the documented `*USER ELEMENT, VARIABLES` requirement for a positive allocation.
- why_minimal_fix_is_allowed: the correction preserves the existing kernel formulation and only fixes the Abaqus-facing call boundary, state-variable allocation check, and static-procedure `LFLAGS` validation.
## Correction Scope
| file | change_type | reason | in_scope |
| --- | --- | --- | --- |
| `src/fortran/uel_3d_euler_beam_uel.for` | source | add fixed-form Abaqus `UEL` wrapper with manual signature, `ABA_PARAM.INC`, manual dimensions, adapter call, and fatal `XIT` path | true |
| `src/fortran/uel_3d_euler_beam_abi_adapter.f90` | source | accept `NSVARS>=1`, use explicit/manual-style array bounds, and validate `LFLAGS(1)` / `LFLAGS(4)` | true |
| `src/fortran/uel_3d_euler_beam_kernel.f90` | source | add status ids for unsupported `LFLAGS(1)` and `LFLAGS(4)` | true |
| `scripts/test_uel_3d_euler_beam_uel.py` | test | verify wrapper signature, include, dimensions, adapter call, and fatal path without requiring Abaqus include files | true |
| `tests/test_uel_3d_euler_beam_uel.py` | test | expose a related test path for the production-source TDD guard | true |
| `tests/fortran/uel_3d_euler_beam/test_abi_static.f90` | test | update valid adapter calls to `NSVARS=1` and explicit static `LFLAGS` values | true |
| `tests/fortran/uel_3d_euler_beam/test_invalid_inputs.f90` | test | add `NSVARS<1`, `LFLAGS(1)`, and `LFLAGS(4)` diagnostics | true |
| `tests/fortran/test_uel_3d_euler_beam_abi_adapter.f90` | test | update source-smoke adapter call to the corrected policy | true |
| `docs/io-definitions/uel-3d-euler-beam.md` | documentation | align current interface contract with the implemented Abaqus-facing policy | true |
| `docs/reference-models/uel-3d-euler-beam.md` | documentation | align test model and input-deck requirements with `VARIABLES=1` and wrapper source-smoke | true |
Excluded files:
- reference artifacts: unchanged
- tolerances: unchanged
- formulation matrix/residual equations: unchanged
## Verification Evidence
| order | command | exit_code | result | evidence |
| --- | --- | --- | --- | --- |
| 1 | `python -m unittest scripts.test_uel_3d_euler_beam_uel` before implementation | 1 | RED | failed because `src/fortran/uel_3d_euler_beam_uel.for` did not exist |
| 2 | `python scripts/validate_fortran.py` before implementation | 1 | RED | `uel_3d_euler_beam_abi_static` failed because `NSVARS=1` returned `UEL3DEB_E006_NSVARS` |
| 3 | `python -m unittest scripts.test_uel_3d_euler_beam_uel` after implementation | 0 | pass | wrapper source-smoke passed |
| 4 | `python scripts/validate_fortran.py` after implementation | 0 | pass | all six no-Abaqus Fortran manifest executables compiled and passed with Intel `ifx` |
| 5 | `python -m unittest discover -s scripts -p "test_*.py"` | 0 | pass | 60 Python harness/source-smoke tests passed |
| 6 | `python scripts/validate_workspace.py` | 0 | pass | reference artifact metadata validation and Fortran validation succeeded |
| 7 | `git diff --check` | 0 | pass | no whitespace errors; line-ending warnings only |
## Traceability
| requirement_or_finding | corrected_file | acceptance_criterion |
| --- | --- | --- |
| C-UEL-001 / C-UEL-002 | `src/fortran/uel_3d_euler_beam_uel.for` | top-level external `SUBROUTINE UEL(...)` exists with manual ABI arguments |
| C-UEL-003 | `src/fortran/uel_3d_euler_beam_uel.for`, `src/fortran/uel_3d_euler_beam_abi_adapter.f90` | Abaqus-facing boundary uses manual dimensions; adapter remains testable |
| C-UEL-004 | `src/fortran/uel_3d_euler_beam_abi_adapter.f90`, `docs/io-definitions/uel-3d-euler-beam.md` | valid static adapter calls use `NSVARS=1`; `NSVARS<1` returns `UEL3DEB_E006_NSVARS` |
| C-UEL-005 | `src/fortran/uel_3d_euler_beam_abi_adapter.f90` | unsupported `LFLAGS(1)` and `LFLAGS(4)` return dedicated diagnostics |
## Handoff Recommendation
| target_agent | reason | required_input |
| --- | --- | --- |
| Build/Test Executor Agent | run full workspace validation after correction | this report and command evidence |
| Reference Verification Agent | update external model expectations to `VARIABLES=1` before user-generated Abaqus artifacts are prepared | corrected I/O and reference-model documents |
| Physics Evaluation Agent | no formulation behavior changed; external CSV physics checks remain pending | future user-provided reference bundles |
## Stop Condition
- repeated_failure: false
- upstream_ambiguity: false
- reference_artifact_gap: true
- environment_blocker: false
- next_required_decision: user must still provide external Abaqus artifacts before solver-result validation can complete
+11 -7
View File
@@ -54,7 +54,7 @@ The wrapper must keep `aba_param.inc` / `ABA_PARAM.INC` as an Abaqus/Standard in
| `MCRD` | `>= 3` | read only `COORDS(1:3,1:2)` |
| `NPROPS` | `9` | reject anything else |
| `NJPROP` | `0` | first scope uses no integer properties |
| `NSVARS` | `0` | first scope stores no persistent state |
| `NSVARS` | `>= 1` | Abaqus `*USER ELEMENT` state-variable allocation must be positive; first scope leaves allocated `SVARS` entries unchanged |
| `NRHS` | `1` | reject multi-RHS procedures |
| `MLVARX` | `>= 12` | required for `RHS(1:12,1)` and `DU(1:12,1)` addressing |
| `NDLOAD` | `0` | element-generated distributed loads are unsupported |
@@ -137,7 +137,7 @@ The reference model plan should use this `.inp` subset.
| `*ELEMENT` | supported | `TYPE=U1` or the approved user element type, optional `ELSET` | two-node UEL connectivity | connectivity order defines local axis 1 |
| `*ELSET` | supported | `ELSET` | element set | needed for `*UEL PROPERTY` |
| `*NSET` | supported | `NSET` | node set | recommended for BC/load/output selection |
| `*USER ELEMENT` | supported | `TYPE=U1`, `NODES=2`, `COORDINATES=3`, `PROPERTIES=9`, `VARIABLES=0` | UEL declaration | omit `UNSYMM`; omit `I PROPERTIES` |
| `*USER ELEMENT` | supported | `TYPE=U1`, `NODES=2`, `COORDINATES=3`, `PROPERTIES=9`, `VARIABLES=1` | UEL declaration | omit `UNSYMM`; omit `I PROPERTIES`; `SVARS(1)` is unused |
| `*UEL PROPERTY` | supported | `ELSET` | real property assignment | data order is `PROPS(1:9)` |
| `*BOUNDARY` | supported | standard nodal DOF constraints | essential boundary conditions | DOFs 1-6 allowed |
| `*CLOAD` | supported | standard nodal concentrated load | external nodal loads/moments | loads are assembled by Abaqus outside UEL |
@@ -161,7 +161,7 @@ Unsupported in first scope:
The reference input deck should declare the user element with this logical content:
```text
*USER ELEMENT, TYPE=U1, NODES=2, COORDINATES=3, PROPERTIES=9, VARIABLES=0
*USER ELEMENT, TYPE=U1, NODES=2, COORDINATES=3, PROPERTIES=9, VARIABLES=1
1, 2, 3, 4, 5, 6
```
@@ -183,11 +183,11 @@ Line breaks may follow Abaqus input formatting rules, but the logical property o
| --- | --- | --- |
| `RHS(MLVARX,*)` | output | Initialize requested storage to zero every call; for supported residual requests, set `RHS(1:12,1) = -K_global*U(1:12)`. |
| `AMATRX(NDOFEL,NDOFEL)` | output | Initialize all `12x12` entries to zero every call; for supported stiffness requests, set all entries to `K_global`. |
| `SVARS(*)` | input/output | unused; require `NSVARS=0`; do not store diagnostics in first scope. |
| `SVARS(*)` | input/output | unused; require `NSVARS>=1` for Abaqus keyword compatibility; do not store diagnostics in first scope. |
| `ENERGY(8)` | input/output | set `ENERGY(1:8)=0.0` every supported call; do not use as validation evidence in first scope. |
| `NDOFEL` | input | must equal `12`. |
| `NRHS` | input | must equal `1`. |
| `NSVARS` | input | must equal `0`. |
| `NSVARS` | input | must be at least `1`. |
| `PROPS(*)` | input | must contain the 9 real values defined above. |
| `NPROPS` | input | must equal `9`. |
| `COORDS(MCRD,NNODE)` | input | read `COORDS(1:3,1)` and `COORDS(1:3,2)` as original global node coordinates. |
@@ -218,6 +218,8 @@ The first-scope wrapper supports only small-displacement static calls:
- require `LFLAGS(2)=0`
- support `LFLAGS(3)=1`, `2`, or `5`
- require `LFLAGS(1)=1` or `2` for static procedure calls
- require `LFLAGS(4)=0` for general-step behavior, not perturbation-only output
- require `NRHS=1`
Contribution rules:
@@ -242,7 +244,7 @@ Invalid input must be caught before matrix assembly. The implementation may choo
| `UEL3DEB-E003` | `MCRD < 3` | fatal missing 3D coordinates |
| `UEL3DEB-E004` | `NPROPS /= 9` | fatal wrong real property count |
| `UEL3DEB-E005` | `NJPROP /= 0` | fatal unsupported integer properties |
| `UEL3DEB-E006` | `NSVARS /= 0` | fatal unsupported state variables |
| `UEL3DEB-E006` | `NSVARS < 1` | fatal missing Abaqus state-variable allocation |
| `UEL3DEB-E007` | `NRHS /= 1` | fatal unsupported RHS count |
| `UEL3DEB-E008` | `MLVARX < 12` | fatal invalid RHS/DU leading dimension |
| `UEL3DEB-E009` | any required coordinate, `U`, or `PROPS` value is NaN or infinite | fatal nonfinite input |
@@ -253,6 +255,8 @@ Invalid input must be caught before matrix assembly. The implementation may choo
| `UEL3DEB-E014` | `LFLAGS(2) /= 0` | fatal unsupported large-displacement or non-small-displacement request |
| `UEL3DEB-E015` | unsupported `LFLAGS(3)` | fatal unsupported contribution request |
| `UEL3DEB-E016` | `NDLOAD /= 0` | fatal unsupported element load input |
| `UEL3DEB-E017` | `LFLAGS(1)` is not `1` or `2` | fatal unsupported non-static procedure request |
| `UEL3DEB-E018` | `LFLAGS(4) /= 0` | fatal unsupported perturbation-output request |
For valid supported calls, the wrapper should set deterministic outputs for the requested contribution and should not modify `PNEWDT`.
@@ -358,7 +362,7 @@ Each external reference model must include metadata fields sufficient for `scrip
### Reference Model Agent
- Use the keyword subset and property order in this document for `model.inp` examples.
- Include no-Abaqus tests for exact `PROPS(1:9)` mapping, `NJPROP=0`, `NSVARS=0`, `NDOFEL=12`, `NNODE=2`, and active DOF order.
- Include no-Abaqus tests for exact `PROPS(1:9)` mapping, `NJPROP=0`, `NSVARS>=1`, `NDOFEL=12`, `NNODE=2`, and active DOF order.
- Include negative tests for every `UEL3DEB-E###` validation rule that is practical in the no-Abaqus harness.
- Define benchmark-specific CSV filenames and near-zero absolute reaction tolerances.
- Keep external artifact generation outside this repository.
+7 -5
View File
@@ -31,7 +31,7 @@ Production source paths planned for later steps:
| --- | --- | --- |
| `src/fortran/uel_3d_euler_beam_kernel.f90` | pure/testable beam geometry, local stiffness, transform, residual, and validation logic | compiled by all no-Abaqus kernel and ABI adapter tests |
| `src/fortran/uel_3d_euler_beam_abi_adapter.f90` | Abaqus-array adapter logic without the literal Abaqus `UEL` entry point | compiled by no-Abaqus ABI mapping tests |
| `src/fortran/uel_3d_euler_beam_uel.for` | thin Abaqus/Standard fixed-form `UEL` wrapper preserving the manual signature and `ABA_PARAM.INC` include | not required for no-Abaqus compile in first RED tests; later wrapper smoke can be added when an Abaqus-compatible include environment is available |
| `src/fortran/uel_3d_euler_beam_uel.for` | thin Abaqus/Standard fixed-form `UEL` wrapper preserving the manual signature and `ABA_PARAM.INC` include | checked by Python source-smoke because the file depends on Abaqus-provided include and `XIT` |
Planned test source paths for step 6:
@@ -43,6 +43,7 @@ Planned test source paths for step 6:
| `tests/fortran/uel_3d_euler_beam/test_kernel_transform_modes.f90` | transformation, symmetry, reversed node order, and rigid-body mode tests |
| `tests/fortran/uel_3d_euler_beam/test_abi_static.f90` | no-Abaqus ABI adapter tests for `RHS`, `AMATRX`, `ENERGY`, dimensions, and `LFLAGS` |
| `tests/fortran/uel_3d_euler_beam/test_invalid_inputs.f90` | validation diagnostics and unsupported-request tests |
| `scripts/test_uel_3d_euler_beam_uel.py` | source-smoke contract for the fixed-form Abaqus `UEL` wrapper signature and dimensions |
Step 6 RED expectation:
@@ -123,10 +124,10 @@ Step 6 should create this planned manifest:
| NOA-A-RHS-001 | `uel_3d_euler_beam_abi_static` | ABI/residual | verify `RHS(1:12,1)=-K_global*U(1:12)` for `LFLAGS(3)=1` and `5` | compile failure or wrong sign | REQ-009 |
| NOA-A-AMATRX-001 | `uel_3d_euler_beam_abi_static` | ABI/stiffness | verify `AMATRX=K_global` for `LFLAGS(3)=1` and `2` and zeroed matrix for residual-only requests | compile failure or mapping mismatch | REQ-003, REQ-004, REQ-008 |
| NOA-A-PROPS-001 | `uel_3d_euler_beam_abi_static` | ABI/property mapping | verify `PROPS(1:9)` maps to `E,G,A,Iy,Iz,J,a_ref_1,a_ref_2,a_ref_3` exactly | compile failure or property permutation mismatch | REQ-005, REQ-007 |
| NOA-A-ENERGY-001 | `uel_3d_euler_beam_abi_static` | ABI/output policy | verify `ENERGY(1:8)=0.0`, `NSVARS=0`, and `PNEWDT` is not changed for valid supported calls | compile failure or output policy mismatch | REQ-010 |
| NOA-A-ENERGY-001 | `uel_3d_euler_beam_abi_static` | ABI/output policy | verify `ENERGY(1:8)=0.0`, `NSVARS>=1`, and `PNEWDT` is not changed for valid supported calls | compile failure or output policy mismatch | REQ-010 |
| NOA-I-SHAPE-001 | `uel_3d_euler_beam_invalid_inputs` | negative | verify diagnostics for `NDOFEL`, `NNODE`, `MCRD`, `NPROPS`, `NJPROP`, `NSVARS`, `NRHS`, `MLVARX` violations | compile failure or missing diagnostic | REQ-003, REQ-006 |
| NOA-I-PHYS-001 | `uel_3d_euler_beam_invalid_inputs` | negative | verify diagnostics for nonfinite coordinates/properties, nonpositive `E,G,A,Iy,Iz,J`, zero length, zero orientation, and near-parallel orientation | compile failure or missing diagnostic | REQ-006, REQ-007 |
| NOA-I-LFLAGS-001 | `uel_3d_euler_beam_invalid_inputs` | negative | verify diagnostics for unsupported `LFLAGS(2)`, unsupported `LFLAGS(3)`, and `NDLOAD /= 0` | compile failure or missing diagnostic | REQ-010 |
| NOA-I-LFLAGS-001 | `uel_3d_euler_beam_invalid_inputs` | negative | verify diagnostics for unsupported `LFLAGS(1)`, unsupported `LFLAGS(2)`, unsupported `LFLAGS(3)`, unsupported `LFLAGS(4)`, and `NDLOAD /= 0` | compile failure or missing diagnostic | REQ-010 |
Default no-Abaqus tolerances:
@@ -143,10 +144,11 @@ The production `UEL` wrapper itself should remain a thin Abaqus ABI entry point.
The ABI adapter tests must verify:
- `NDOFEL=12`, `NNODE=2`, `MCRD>=3`, `NPROPS=9`, `NJPROP=0`, `NSVARS=0`, `NRHS=1`, and `MLVARX>=12`
- `NDOFEL=12`, `NNODE=2`, `MCRD>=3`, `NPROPS=9`, `NJPROP=0`, `NSVARS>=1`, `NRHS=1`, and `MLVARX>=12`
- `COORDS(1:3,1:2)` maps to `X1`, `X2`
- `U(1:12)` maps to the global kernel vector without permutation
- `PROPS(1:9)` maps exactly to the interface contract
- `LFLAGS(1)=1|2`, `LFLAGS(2)=0`, `LFLAGS(3)=1|2|5`, and `LFLAGS(4)=0` select the supported static general-step path
- `LFLAGS(3)=1`, `2`, and `5` select the expected `AMATRX` and `RHS` outputs
- non-requested `AMATRX` or `RHS` storage is deterministically zeroed by the no-Abaqus adapter
- `ENERGY(1:8)` is zeroed and `SVARS` is unused
@@ -252,7 +254,7 @@ Every external `model.inp` must stay within the supported keyword subset from `d
- `*ELEMENT`
- `*ELSET`
- `*NSET`
- `*USER ELEMENT, TYPE=U1, NODES=2, COORDINATES=3, PROPERTIES=9, VARIABLES=0`
- `*USER ELEMENT, TYPE=U1, NODES=2, COORDINATES=3, PROPERTIES=9, VARIABLES=1`
- active DOF line: `1, 2, 3, 4, 5, 6`
- `*UEL PROPERTY, ELSET=<uel_element_set>` with data order `E,G,A,Iy,Iz,J,a_ref_1,a_ref_2,a_ref_3`
- `*BOUNDARY`
+99
View File
@@ -0,0 +1,99 @@
import re
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
WRAPPER = ROOT / "src" / "fortran" / "uel_3d_euler_beam_uel.for"
def normalized_source() -> str:
if not WRAPPER.exists():
raise AssertionError(f"missing Abaqus UEL wrapper: {WRAPPER}")
return re.sub(r"\s+", " ", WRAPPER.read_text(encoding="utf-8").upper())
class Uel3dEulerBeamWrapperContractTests(unittest.TestCase):
def test_wrapper_file_exists(self):
self.assertTrue(WRAPPER.exists(), f"missing Abaqus UEL wrapper: {WRAPPER}")
def test_wrapper_preserves_manual_uel_signature(self):
text = normalized_source()
self.assertIn("SUBROUTINE UEL(", text)
self.assertIn("INCLUDE 'ABA_PARAM.INC'", text)
for token in (
"RHS",
"AMATRX",
"SVARS",
"ENERGY",
"NDOFEL",
"NRHS",
"NSVARS",
"PROPS",
"NPROPS",
"COORDS",
"MCRD",
"NNODE",
"U",
"DU",
"V",
"A",
"JTYPE",
"TIME",
"DTIME",
"KSTEP",
"KINC",
"JELEM",
"PARAMS",
"NDLOAD",
"JDLTYP",
"ADLMAG",
"PREDEF",
"NPREDF",
"LFLAGS",
"MLVARX",
"DDLMAG",
"MDLOAD",
"PNEWDT",
"JPROPS",
"NJPROP",
"PERIOD",
):
self.assertRegex(text, rf"\b{token}\b")
signature = text.split("INCLUDE 'ABA_PARAM.INC'", 1)[0]
self.assertNotRegex(signature, r"\bSTATUS\b")
def test_wrapper_uses_abaqus_dimensions_and_thin_adapter_call(self):
text = normalized_source()
for shape in (
"RHS(MLVARX,*)",
"AMATRX(NDOFEL,NDOFEL)",
"SVARS(*)",
"ENERGY(8)",
"PROPS(*)",
"COORDS(MCRD,NNODE)",
"U(NDOFEL)",
"DU(MLVARX,*)",
"V(NDOFEL)",
"A(NDOFEL)",
"TIME(2)",
"PARAMS(*)",
"JDLTYP(MDLOAD,*)",
"ADLMAG(MDLOAD,*)",
"DDLMAG(MDLOAD,*)",
"PREDEF(2,NPREDF,NNODE)",
"LFLAGS(*)",
"JPROPS(*)",
):
self.assertIn(shape, text)
self.assertIn("USE UEL_3D_EULER_BEAM_ABI_ADAPTER", text)
self.assertIn("CALL UEL3DEB_ABI_STATIC", text)
self.assertIn("CALL XIT", text)
if __name__ == "__main__":
unittest.main()
+22 -13
View File
@@ -7,6 +7,7 @@ module uel_3d_euler_beam_abi_adapter
UEL3DEB_E006_NSVARS, UEL3DEB_E007_NRHS, UEL3DEB_E008_MLVARX, &
UEL3DEB_E009_NONFINITE, UEL3DEB_E014_LFLAGS2, &
UEL3DEB_E015_LFLAGS3, UEL3DEB_E016_NDLOAD, &
UEL3DEB_E017_LFLAGS1, UEL3DEB_E018_LFLAGS4, &
uel3deb_validate_kernel_inputs, uel3deb_global_stiffness
implicit none
@@ -22,24 +23,24 @@ contains
props, nprops, coords, mcrd, nnode, u, &
lflags, mlvarx, ndload, pnewdt, jprops, &
njprop, status)
real(dp), intent(inout) :: rhs(:, :)
real(dp), intent(inout) :: amatrx(:, :)
real(dp), intent(inout) :: energy(:)
integer, intent(in) :: ndofel
integer, intent(in) :: nrhs
integer, intent(in) :: nsvars
real(dp), intent(in) :: props(:)
integer, intent(in) :: nprops
real(dp), intent(in) :: coords(:, :)
integer, intent(in) :: mcrd
integer, intent(in) :: nnode
real(dp), intent(in) :: u(:)
integer, intent(in) :: lflags(:)
integer, intent(in) :: mlvarx
integer, intent(in) :: ndload
real(dp), intent(inout) :: pnewdt
integer, intent(in) :: jprops(:)
integer, intent(in) :: njprop
real(dp), intent(inout) :: rhs(mlvarx, *)
real(dp), intent(inout) :: amatrx(ndofel, ndofel)
real(dp), intent(inout) :: energy(8)
real(dp), intent(in) :: props(*)
real(dp), intent(in) :: coords(mcrd, nnode)
real(dp), intent(in) :: u(ndofel)
integer, intent(in) :: lflags(*)
real(dp), intent(inout) :: pnewdt
integer, intent(in) :: jprops(*)
integer, intent(out) :: status
real(dp) :: coords3(3, 2)
real(dp) :: props9(9)
@@ -49,9 +50,9 @@ contains
real(dp) :: residual(12)
status = UEL3DEB_OK
rhs = 0.0_dp
amatrx = 0.0_dp
energy = 0.0_dp
if (mlvarx > 0 .and. nrhs > 0) rhs(1:mlvarx, 1:nrhs) = 0.0_dp
if (ndofel > 0) amatrx(1:ndofel, 1:ndofel) = 0.0_dp
energy(1:8) = 0.0_dp
if (ndofel /= 12) then
status = UEL3DEB_E001_NDOFEL
@@ -73,7 +74,7 @@ contains
status = UEL3DEB_E005_NJPROP
return
end if
if (nsvars /= 0) then
if (nsvars < 1) then
status = UEL3DEB_E006_NSVARS
return
end if
@@ -85,6 +86,10 @@ contains
status = UEL3DEB_E008_MLVARX
return
end if
if (.not. any(lflags(1) == [1, 2])) then
status = UEL3DEB_E017_LFLAGS1
return
end if
if (lflags(2) /= 0) then
status = UEL3DEB_E014_LFLAGS2
return
@@ -93,6 +98,10 @@ contains
status = UEL3DEB_E015_LFLAGS3
return
end if
if (lflags(4) /= 0) then
status = UEL3DEB_E018_LFLAGS4
return
end if
if (ndload /= 0) then
status = UEL3DEB_E016_NDLOAD
return
+2
View File
@@ -24,6 +24,8 @@ module uel_3d_euler_beam_kernel
integer, parameter, public :: UEL3DEB_E014_LFLAGS2 = 14
integer, parameter, public :: UEL3DEB_E015_LFLAGS3 = 15
integer, parameter, public :: UEL3DEB_E016_NDLOAD = 16
integer, parameter, public :: UEL3DEB_E017_LFLAGS1 = 17
integer, parameter, public :: UEL3DEB_E018_LFLAGS4 = 18
public :: uel3deb_validate_kernel_inputs
public :: uel3deb_local_stiffness
+31
View File
@@ -0,0 +1,31 @@
SUBROUTINE UEL(RHS, AMATRX, SVARS, ENERGY, NDOFEL,
1 NRHS, NSVARS, PROPS, NPROPS, COORDS, MCRD, NNODE,
2 U, DU, V, A, JTYPE, TIME, DTIME, KSTEP, KINC, JELEM,
3 PARAMS, NDLOAD, JDLTYP, ADLMAG, PREDEF, NPREDF,
4 LFLAGS, MLVARX, DDLMAG, MDLOAD, PNEWDT, JPROPS,
5 NJPROP, PERIOD)
USE uel_3d_euler_beam_abi_adapter, ONLY:
1 uel3deb_abi_static
INCLUDE 'ABA_PARAM.INC'
DIMENSION RHS(MLVARX,*), AMATRX(NDOFEL,NDOFEL),
1 SVARS(*), ENERGY(8), PROPS(*), COORDS(MCRD,NNODE),
2 U(NDOFEL), DU(MLVARX,*), V(NDOFEL), A(NDOFEL),
3 TIME(2), PARAMS(*), JDLTYP(MDLOAD,*),
4 ADLMAG(MDLOAD,*), DDLMAG(MDLOAD,*),
5 PREDEF(2,NPREDF,NNODE), LFLAGS(*), JPROPS(*)
INTEGER STATUS
CALL uel3deb_abi_static(RHS, AMATRX, ENERGY, NDOFEL,
1 NRHS, NSVARS, PROPS, NPROPS, COORDS, MCRD, NNODE,
2 U, LFLAGS, MLVARX, NDLOAD, PNEWDT, JPROPS, NJPROP,
3 STATUS)
IF (STATUS .NE. 0) THEN
WRITE(7,*) 'UEL3DEB fatal status=', STATUS,
1 ' JELEM=', JELEM, ' KSTEP=', KSTEP,
2 ' KINC=', KINC
CALL XIT
END IF
RETURN
END
@@ -22,10 +22,12 @@ program test_uel_3d_euler_beam_abi_adapter
u = 0.0_dp
pnewdt = 1.0_dp
lflags = 0
lflags(1) = 1
lflags(3) = 2
lflags(4) = 0
jprops = 0
call uel3deb_abi_static(rhs, amatrx, energy, 12, 1, 0, props, 9, coords, &
call uel3deb_abi_static(rhs, amatrx, energy, 12, 1, 1, props, 9, coords, &
3, 2, u, lflags, 12, 0, pnewdt, jprops, 0, status)
call assert_equal_int(status, UEL3DEB_OK, 'abi adapter source smoke status')
call assert_matrix_symmetric(amatrx, tol_symmetry, 'abi adapter source smoke K')
@@ -57,11 +57,13 @@ contains
amatrx = 999.0_dp
energy = 999.0_dp
lflags = 0
lflags(1) = 1
lflags(2) = 0
lflags(3) = lflags3
lflags(4) = 0
jprops = 0
call uel3deb_abi_static(rhs, amatrx, energy, 12, 1, 0, props, 9, coords, &
call uel3deb_abi_static(rhs, amatrx, energy, 12, 1, 1, props, 9, coords, &
3, 2, u, lflags, 12, 0, pnewdt, jprops, 0, status)
end subroutine call_adapter
@@ -8,6 +8,7 @@ program test_invalid_inputs
UEL3DEB_E010_NONPOSITIVE_PROPERTY, UEL3DEB_E011_ZERO_LENGTH, &
UEL3DEB_E012_ZERO_ORIENTATION, UEL3DEB_E013_PARALLEL_ORIENTATION, &
UEL3DEB_E014_LFLAGS2, UEL3DEB_E015_LFLAGS3, UEL3DEB_E016_NDLOAD, &
UEL3DEB_E017_LFLAGS1, UEL3DEB_E018_LFLAGS4, &
uel3deb_validate_kernel_inputs
use uel_3d_euler_beam_abi_adapter, only: uel3deb_abi_static
implicit none
@@ -60,32 +61,37 @@ contains
end subroutine test_kernel_physical_diagnostics
subroutine test_adapter_shape_and_request_diagnostics()
call expect_adapter_status(11, 1, 0, 9, 3, 2, 12, 0, 0, 0, 1, &
call expect_adapter_status(11, 1, 1, 9, 3, 2, 12, 0, 0, 1, 0, 0, 1, &
UEL3DEB_E001_NDOFEL, 'NOA-I-SHAPE-001 NDOFEL')
call expect_adapter_status(12, 1, 0, 9, 3, 1, 12, 0, 0, 0, 1, &
call expect_adapter_status(12, 1, 1, 9, 3, 1, 12, 0, 0, 1, 0, 0, 1, &
UEL3DEB_E002_NNODE, 'NOA-I-SHAPE-001 NNODE')
call expect_adapter_status(12, 1, 0, 9, 2, 2, 12, 0, 0, 0, 1, &
call expect_adapter_status(12, 1, 1, 9, 2, 2, 12, 0, 0, 1, 0, 0, 1, &
UEL3DEB_E003_MCRD, 'NOA-I-SHAPE-001 MCRD')
call expect_adapter_status(12, 1, 0, 8, 3, 2, 12, 0, 0, 0, 1, &
call expect_adapter_status(12, 1, 1, 8, 3, 2, 12, 0, 0, 1, 0, 0, 1, &
UEL3DEB_E004_NPROPS, 'NOA-I-SHAPE-001 NPROPS')
call expect_adapter_status(12, 1, 0, 9, 3, 2, 12, 0, 1, 0, 1, &
call expect_adapter_status(12, 1, 1, 9, 3, 2, 12, 0, 1, 1, 0, 0, 1, &
UEL3DEB_E005_NJPROP, 'NOA-I-SHAPE-001 NJPROP')
call expect_adapter_status(12, 1, 1, 9, 3, 2, 12, 0, 0, 0, 1, &
call expect_adapter_status(12, 1, 0, 9, 3, 2, 12, 0, 0, 1, 0, 0, 1, &
UEL3DEB_E006_NSVARS, 'NOA-I-SHAPE-001 NSVARS')
call expect_adapter_status(12, 2, 0, 9, 3, 2, 12, 0, 0, 0, 1, &
call expect_adapter_status(12, 2, 1, 9, 3, 2, 12, 0, 0, 1, 0, 0, 1, &
UEL3DEB_E007_NRHS, 'NOA-I-SHAPE-001 NRHS')
call expect_adapter_status(12, 1, 0, 9, 3, 2, 11, 0, 0, 0, 1, &
call expect_adapter_status(12, 1, 1, 9, 3, 2, 11, 0, 0, 1, 0, 0, 1, &
UEL3DEB_E008_MLVARX, 'NOA-I-SHAPE-001 MLVARX')
call expect_adapter_status(12, 1, 0, 9, 3, 2, 12, 0, 0, 1, 1, &
call expect_adapter_status(12, 1, 1, 9, 3, 2, 12, 0, 0, 3, 0, 0, 1, &
UEL3DEB_E017_LFLAGS1, 'NOA-I-LFLAGS-001 LFLAGS1')
call expect_adapter_status(12, 1, 1, 9, 3, 2, 12, 0, 0, 1, 1, 0, 1, &
UEL3DEB_E014_LFLAGS2, 'NOA-I-LFLAGS-001 LFLAGS2')
call expect_adapter_status(12, 1, 0, 9, 3, 2, 12, 0, 0, 0, 3, &
call expect_adapter_status(12, 1, 1, 9, 3, 2, 12, 0, 0, 1, 0, 3, 1, &
UEL3DEB_E015_LFLAGS3, 'NOA-I-LFLAGS-001 LFLAGS3')
call expect_adapter_status(12, 1, 0, 9, 3, 2, 12, 1, 0, 0, 1, &
call expect_adapter_status(12, 1, 1, 9, 3, 2, 12, 0, 0, 1, 0, 1, 1, &
UEL3DEB_E018_LFLAGS4, 'NOA-I-LFLAGS-001 LFLAGS4')
call expect_adapter_status(12, 1, 1, 9, 3, 2, 12, 1, 0, 1, 0, 1, 0, &
UEL3DEB_E016_NDLOAD, 'NOA-I-LFLAGS-001 NDLOAD')
end subroutine test_adapter_shape_and_request_diagnostics
subroutine expect_adapter_status(ndofel, nrhs, nsvars, nprops, mcrd, nnode, &
mlvarx, ndload, njprop, lflag2, lflag3, &
mlvarx, ndload, njprop, lflag1, lflag2, &
lflag3, lflag4, &
expected_status, message)
integer, intent(in) :: ndofel
integer, intent(in) :: nrhs
@@ -96,8 +102,10 @@ contains
integer, intent(in) :: mlvarx
integer, intent(in) :: ndload
integer, intent(in) :: njprop
integer, intent(in) :: lflag1
integer, intent(in) :: lflag2
integer, intent(in) :: lflag3
integer, intent(in) :: lflag4
integer, intent(in) :: expected_status
character(len=*), intent(in) :: message
real(dp) :: rhs(12, 2)
@@ -118,8 +126,10 @@ contains
u = 0.0_dp
pnewdt = 1.0_dp
lflags = 0
lflags(1) = lflag1
lflags(2) = lflag2
lflags(3) = lflag3
lflags(4) = lflag4
jprops = 0
call uel3deb_abi_static(rhs, amatrx, energy, ndofel, nrhs, nsvars, props, &
+4
View File
@@ -0,0 +1,4 @@
from scripts.test_uel_3d_euler_beam_uel import Uel3dEulerBeamWrapperContractTests
__all__ = ["Uel3dEulerBeamWrapperContractTests"]