test: add 3d euler beam red fortran tests

This commit is contained in:
김경종
2026-06-11 14:06:36 +09:00
parent a07a92ed4b
commit a3b5d7d383
11 changed files with 830 additions and 10 deletions
+3 -3
View File
@@ -12,7 +12,7 @@
- Phase directory: `phases/uel-3d-euler-beam` - Phase directory: `phases/uel-3d-euler-beam`
- Current planned entry point: `python scripts/execute.py uel-3d-euler-beam` - Current planned entry point: `python scripts/execute.py uel-3d-euler-beam`
- First pending step: `step0` requirements - First pending step: `step7` Fortran implementation
## Planned Steps ## Planned Steps
@@ -22,8 +22,8 @@
4. Numerical review: `docs/numerical-reviews/uel-3d-euler-beam.md` 4. Numerical review: `docs/numerical-reviews/uel-3d-euler-beam.md`
5. Interface contract: `docs/io-definitions/uel-3d-euler-beam.md` 5. Interface contract: `docs/io-definitions/uel-3d-euler-beam.md`
6. Test/reference model plan: `docs/reference-models/uel-3d-euler-beam.md` 6. Test/reference model plan: `docs/reference-models/uel-3d-euler-beam.md`
7. RED no-Abaqus Fortran tests and manifest 7. RED no-Abaqus Fortran tests and manifest - completed
8. GREEN Fortran implementation 8. GREEN Fortran implementation - next
9. Validation, physics sanity, and readiness audit 9. Validation, physics sanity, and readiness audit
## Success Criteria ## Success Criteria
+21 -6
View File
@@ -5,8 +5,8 @@
- Active objective: 3D Euler-Bernoulli beam Abaqus/Standard `UEL` - Active objective: 3D Euler-Bernoulli beam Abaqus/Standard `UEL`
- Active phase: `phases/uel-3d-euler-beam` - Active phase: `phases/uel-3d-euler-beam`
- Active owner: unassigned - Active owner: unassigned
- Current status: completed `phases/uel-3d-euler-beam/step5.md` test/reference model planning step - Current status: completed `phases/uel-3d-euler-beam/step6.md` RED no-Abaqus test creation step
- Next action: execute `phases/uel-3d-euler-beam/step6.md` RED no-Abaqus test creation step - Next action: execute `phases/uel-3d-euler-beam/step7.md` Fortran implementation step
## Completed ## Completed
@@ -31,6 +31,15 @@
- Completed step 5 test/reference model planning for `uel-3d-euler-beam`. - Completed step 5 test/reference model planning for `uel-3d-euler-beam`.
- Created `docs/reference-models/uel-3d-euler-beam.md`. - Created `docs/reference-models/uel-3d-euler-beam.md`.
- Updated `phases/uel-3d-euler-beam/index.json` step 5 to `completed`. - Updated `phases/uel-3d-euler-beam/index.json` step 5 to `completed`.
- Completed step 6 RED no-Abaqus test creation for `uel-3d-euler-beam`.
- Created `tests/fortran/manifest.json`.
- Created `tests/fortran/uel_3d_euler_beam/test_support.f90`.
- Created `tests/fortran/uel_3d_euler_beam/test_kernel_stiffness.f90`.
- Created `tests/fortran/uel_3d_euler_beam/test_kernel_transform_modes.f90`.
- Created `tests/fortran/uel_3d_euler_beam/test_abi_static.f90`.
- Created `tests/fortran/uel_3d_euler_beam/test_invalid_inputs.f90`.
- Created `docs/build-test-reports/uel-3d-euler-beam-red.md`.
- Updated `phases/uel-3d-euler-beam/index.json` step 6 to `completed`.
## In Progress ## In Progress
@@ -42,22 +51,28 @@
## Last Verification ## Last Verification
Latest verification after completing step 5 test/reference model planning: Latest verification after completing step 6 RED no-Abaqus test creation:
```bash ```bash
python -m json.tool tests/fortran/manifest.json
python -m unittest discover -s scripts -p "test_*.py" python -m unittest discover -s scripts -p "test_*.py"
python scripts/validate_reference_artifacts.py python scripts/validate_reference_artifacts.py
python scripts/validate_fortran.py
python scripts/validate_workspace.py python scripts/validate_workspace.py
$env:HARNESS_FORTRAN_VALIDATION='detect'; python scripts/validate_workspace.py
``` ```
Result: all passed. Result: RED evidence recorded.
- `python -m json.tool tests/fortran/manifest.json`: passed.
- `python -m unittest discover -s scripts -p "test_*.py"`: 56 tests passed. - `python -m unittest discover -s scripts -p "test_*.py"`: 56 tests passed.
- `python scripts/validate_reference_artifacts.py`: reference artifact metadata validation succeeded. - `python scripts/validate_reference_artifacts.py`: reference artifact metadata validation succeeded.
- `python scripts/validate_workspace.py`: succeeded; `validate_fortran.py` reported no Fortran validation commands configured because `tests/fortran/manifest.json` does not exist yet. - `python scripts/validate_fortran.py`: default process failed before RED because child `cmd.exe` saw empty `%PATH%`; with minimal `PATH` normalization it reached `ifx` and failed as expected on missing `src/fortran/uel_3d_euler_beam_kernel.f90`.
- `python scripts/validate_workspace.py`: default process failed at the same environment issue; with minimal `PATH` normalization it failed at the expected missing production kernel source.
- `$env:HARNESS_FORTRAN_VALIDATION='detect'; python scripts/validate_workspace.py`: passed.
## Next Agent Checklist ## Next Agent Checklist
- Read `AGENTS.md`, `PLAN.md`, `PROGRESS.md`, and `WORKNOTE.md`. - Read `AGENTS.md`, `PLAN.md`, `PROGRESS.md`, and `WORKNOTE.md`.
- Confirm no other owner is active in this file. - Confirm no other owner is active in this file.
- Start `phases/uel-3d-euler-beam/step6.md`. - Start `phases/uel-3d-euler-beam/step7.md`.
- Update this file when step status changes or before handing off. - Update this file when step status changes or before handing off.
+1
View File
@@ -11,3 +11,4 @@
- PowerShell에서 기본 출력 인코딩에 따라 Korean text가 깨져 보일 수 있다. `Get-Content -Raw -Encoding UTF8 AGENTS.md`처럼 UTF-8을 명시하면 정상 확인 가능하다. - PowerShell에서 기본 출력 인코딩에 따라 Korean text가 깨져 보일 수 있다. `Get-Content -Raw -Encoding UTF8 AGENTS.md`처럼 UTF-8을 명시하면 정상 확인 가능하다.
- `scripts/execute.py`는 phase step을 Codex subprocess로 실행하고, 각 step이 `phases/<phase>/index.json`의 status를 직접 갱신해야 한다. Step 지시문에는 이 요구가 명확해야 한다. - `scripts/execute.py`는 phase step을 Codex subprocess로 실행하고, 각 step이 `phases/<phase>/index.json`의 status를 직접 갱신해야 한다. Step 지시문에는 이 요구가 명확해야 한다.
- `python scripts/validate_workspace.py`는 Fortran manifest가 없으면 `validate_fortran.py`에서 "No Fortran validation commands configured."를 출력하고 성공할 수 있다. 이것은 구현 전 scaffold 상태에서는 정상이다. - `python scripts/validate_workspace.py`는 Fortran manifest가 없으면 `validate_fortran.py`에서 "No Fortran validation commands configured."를 출력하고 성공할 수 있다. 이것은 구현 전 scaffold 상태에서는 정상이다.
- Step 6에서 `tests/fortran/manifest.json`을 추가한 뒤 기본 `python scripts/validate_fortran.py`가 nested `cmd` lookup에서 먼저 실패했다. PowerShell/Python은 `cmd.exe`를 찾지만 child `cmd.exe``%PATH%`를 빈 값으로 보았다. RED compile evidence는 `$env:PATH='C:\Windows\System32;C:\Windows;C:\Users\user\miniforge3'; C:\Users\user\miniforge3\python.exe scripts/validate_fortran.py`로 확인했으며, 이때 `ifx`가 실행되고 missing `src/fortran/uel_3d_euler_beam_kernel.f90` compile failure가 발생했다.
@@ -0,0 +1,67 @@
# 3D Euler-Bernoulli Beam UEL RED Test Report
## Metadata
- feature_id: uel-3d-euler-beam
- source_reference_models: `docs/reference-models/uel-3d-euler-beam.md`
- status: red-evidence-recorded
- owner_agent: implementation-agent
- date: 2026-06-11
## Scope
This report records the step 6 RED evidence after adding no-Abaqus Fortran tests and `tests/fortran/manifest.json`.
Production Fortran source was not added in this step. The expected RED condition is a compile failure from missing planned production source:
- `src/fortran/uel_3d_euler_beam_kernel.f90`
- `src/fortran/uel_3d_euler_beam_abi_adapter.f90`
## Test Files Added Before Production Code
| manifest_test | source file |
| --- | --- |
| `uel_3d_euler_beam_kernel_stiffness` | `tests/fortran/uel_3d_euler_beam/test_kernel_stiffness.f90` |
| `uel_3d_euler_beam_kernel_transform_modes` | `tests/fortran/uel_3d_euler_beam/test_kernel_transform_modes.f90` |
| `uel_3d_euler_beam_abi_static` | `tests/fortran/uel_3d_euler_beam/test_abi_static.f90` |
| `uel_3d_euler_beam_invalid_inputs` | `tests/fortran/uel_3d_euler_beam/test_invalid_inputs.f90` |
| shared support | `tests/fortran/uel_3d_euler_beam/test_support.f90` |
## Command Log Summary
| order | command | exit_code | result | evidence |
| --- | --- | --- | --- | --- |
| 1 | `python -m json.tool tests/fortran/manifest.json` | 0 | pass | manifest JSON parsed successfully |
| 2 | `python -m unittest discover -s scripts -p "test_*.py"` | 0 | pass | 56 Python harness tests passed |
| 3 | `python scripts/validate_reference_artifacts.py` | 0 | pass | reference artifact metadata validation succeeded |
| 4 | `python scripts/validate_fortran.py` | 1 | environment failure before RED | current `cmd.exe` child process sees empty `%PATH%`; nested `cmd` command is not found |
| 5 | `$env:PATH='C:\Windows\System32;C:\Windows;C:\Users\user\miniforge3'; C:\Users\user\miniforge3\python.exe scripts/validate_fortran.py` | 1 | expected RED | `ifx` reached compile and failed because `src/fortran/uel_3d_euler_beam_kernel.f90` does not exist |
| 6 | `python scripts/validate_workspace.py` | 1 | environment failure before RED | reference validation passed, then Fortran validation hit the same nested `cmd` lookup issue |
| 7 | `$env:PATH='C:\Windows\System32;C:\Windows;C:\Users\user\miniforge3'; C:\Users\user\miniforge3\python.exe scripts/validate_workspace.py` | 1 | expected RED | reference validation passed, then Fortran validation failed on missing production kernel source |
| 8 | `$env:HARNESS_FORTRAN_VALIDATION='detect'; python scripts/validate_workspace.py` | 0 | pass | reference validation passed; Fortran manifest/compiler detection mode produced no compile commands |
## RED Failure Classification
- classification: `fortran-compile`
- first implementation-owned failure after environment normalization: missing planned production source
- primary evidence tail:
```text
xfortcom: Severe: No such file or directory
... file is 'C:\git\AbaqusSubroutineDev\src\fortran\uel_3d_euler_beam_kernel.f90'
compilation aborted for C:\git\AbaqusSubroutineDev\src\fortran\uel_3d_euler_beam_kernel.f90 (code 1)
```
The subsequent module import errors in `test_kernel_stiffness.f90` are secondary to the missing kernel module source.
## Environment Note
This Codex process can find `cmd.exe` from PowerShell and Python, but a child `cmd.exe` invoked during `validate_fortran.py` sees an empty `%PATH%` unless a minimal `PATH` is set explicitly. The normalized command above confirms that Intel oneAPI `ifx` is available through the configured oneAPI env script and that the actionable RED failure is the missing production source, not a missing compiler.
## Handoff to Step 7
Implement the minimum production Fortran needed to turn the four manifest tests GREEN:
- `src/fortran/uel_3d_euler_beam_kernel.f90`
- `src/fortran/uel_3d_euler_beam_abi_adapter.f90`
The Abaqus fixed-form `UEL` wrapper remains planned for the implementation gate but is not required to satisfy these no-Abaqus RED tests unless the step 7 plan explicitly includes it.
+2 -1
View File
@@ -41,7 +41,8 @@
{ {
"step": 6, "step": 6,
"name": "tdd-tests", "name": "tdd-tests",
"status": "pending" "status": "completed",
"summary": "Created approved no-Abaqus Fortran manifest and test drivers under tests/fortran/uel_3d_euler_beam. Recorded RED evidence in docs/build-test-reports/uel-3d-euler-beam-red.md: Python harness and reference artifact validation passed; normalized oneAPI Fortran validation reached ifx and failed as expected because planned production kernel source is not implemented yet."
}, },
{ {
"step": 7, "step": 7,
+38
View File
@@ -0,0 +1,38 @@
{
"tests": [
{
"name": "uel_3d_euler_beam_kernel_stiffness",
"sources": [
"tests/fortran/uel_3d_euler_beam/test_support.f90",
"src/fortran/uel_3d_euler_beam_kernel.f90",
"tests/fortran/uel_3d_euler_beam/test_kernel_stiffness.f90"
]
},
{
"name": "uel_3d_euler_beam_kernel_transform_modes",
"sources": [
"tests/fortran/uel_3d_euler_beam/test_support.f90",
"src/fortran/uel_3d_euler_beam_kernel.f90",
"tests/fortran/uel_3d_euler_beam/test_kernel_transform_modes.f90"
]
},
{
"name": "uel_3d_euler_beam_abi_static",
"sources": [
"tests/fortran/uel_3d_euler_beam/test_support.f90",
"src/fortran/uel_3d_euler_beam_kernel.f90",
"src/fortran/uel_3d_euler_beam_abi_adapter.f90",
"tests/fortran/uel_3d_euler_beam/test_abi_static.f90"
]
},
{
"name": "uel_3d_euler_beam_invalid_inputs",
"sources": [
"tests/fortran/uel_3d_euler_beam/test_support.f90",
"src/fortran/uel_3d_euler_beam_kernel.f90",
"src/fortran/uel_3d_euler_beam_abi_adapter.f90",
"tests/fortran/uel_3d_euler_beam/test_invalid_inputs.f90"
]
}
]
}
@@ -0,0 +1,127 @@
program test_abi_static
use uel_3d_euler_beam_test_support
use uel_3d_euler_beam_kernel, only: &
UEL3DEB_OK, uel3deb_global_stiffness
use uel_3d_euler_beam_abi_adapter, only: uel3deb_abi_static
implicit none
call test_static_full_contribution()
call test_stiffness_only_and_residual_only()
write(*, '(A)') 'PASS uel_3d_euler_beam_abi_static'
contains
subroutine valid_problem(coords, props, u)
real(dp), intent(out) :: coords(3, 2)
real(dp), intent(out) :: props(9)
real(dp), intent(out) :: u(12)
coords(:, 1) = [0.0_dp, 0.0_dp, 0.0_dp]
coords(:, 2) = [2.0_dp, 0.0_dp, 0.0_dp]
props = [210.0e9_dp, 80.0e9_dp, 3.0e-3_dp, 4.0e-6_dp, 7.0e-6_dp, &
2.5e-6_dp, 0.0_dp, 1.0_dp, 0.0_dp]
u = [1.0e-4_dp, -2.0e-4_dp, 3.0e-4_dp, 4.0e-5_dp, -5.0e-5_dp, &
6.0e-5_dp, -7.0e-4_dp, 8.0e-4_dp, -9.0e-4_dp, 1.0e-4_dp, &
-1.1e-4_dp, 1.2e-4_dp]
end subroutine valid_problem
subroutine expected_outputs(coords, props, u, expected_k, expected_rhs)
real(dp), intent(in) :: coords(3, 2)
real(dp), intent(in) :: props(9)
real(dp), intent(in) :: u(12)
real(dp), intent(out) :: expected_k(12, 12)
real(dp), intent(out) :: expected_rhs(12)
real(dp) :: transform(12, 12)
integer :: status
call uel3deb_global_stiffness(coords, props, expected_k, transform, status)
call assert_equal_int(status, UEL3DEB_OK, 'expected K status')
expected_rhs = -matmul(expected_k, u)
end subroutine expected_outputs
subroutine call_adapter(lflags3, rhs, amatrx, energy, pnewdt, status)
integer, intent(in) :: lflags3
real(dp), intent(out) :: rhs(12, 1)
real(dp), intent(out) :: amatrx(12, 12)
real(dp), intent(out) :: energy(8)
real(dp), intent(inout) :: pnewdt
integer, intent(out) :: status
real(dp) :: coords(3, 2)
real(dp) :: props(9)
real(dp) :: u(12)
integer :: lflags(5)
integer :: jprops(1)
call valid_problem(coords, props, u)
rhs = 999.0_dp
amatrx = 999.0_dp
energy = 999.0_dp
lflags = 0
lflags(2) = 0
lflags(3) = lflags3
jprops = 0
call uel3deb_abi_static(rhs, amatrx, energy, 12, 1, 0, props, 9, coords, &
3, 2, u, lflags, 12, 0, pnewdt, jprops, 0, status)
end subroutine call_adapter
subroutine test_static_full_contribution()
real(dp) :: coords(3, 2)
real(dp) :: props(9)
real(dp) :: u(12)
real(dp) :: rhs(12, 1)
real(dp) :: amatrx(12, 12)
real(dp) :: energy(8)
real(dp) :: expected_k(12, 12)
real(dp) :: expected_rhs(12)
real(dp) :: pnewdt
integer :: status
call valid_problem(coords, props, u)
call expected_outputs(coords, props, u, expected_k, expected_rhs)
pnewdt = 0.25_dp
call call_adapter(1, rhs, amatrx, energy, pnewdt, status)
call assert_equal_int(status, UEL3DEB_OK, 'NOA-A-AMATRX-001 status LFLAGS3=1')
call assert_matrix_close(amatrx, expected_k, tol_stiff, 'NOA-A-AMATRX-001 K')
call assert_vector_close(rhs(:, 1), expected_rhs, tol_vector, 'NOA-A-RHS-001 RHS')
call assert_vector_close(energy, spread(0.0_dp, 1, 8), tol_vector, 'NOA-A-ENERGY-001 ENERGY')
call assert_close(pnewdt, 0.25_dp, tol_vector, 'NOA-A-ENERGY-001 PNEWDT')
end subroutine test_static_full_contribution
subroutine test_stiffness_only_and_residual_only()
real(dp) :: coords(3, 2)
real(dp) :: props(9)
real(dp) :: u(12)
real(dp) :: rhs(12, 1)
real(dp) :: amatrx(12, 12)
real(dp) :: energy(8)
real(dp) :: expected_k(12, 12)
real(dp) :: expected_rhs(12)
real(dp) :: zeros12(12)
real(dp) :: zeros_k(12, 12)
real(dp) :: pnewdt
integer :: status
call valid_problem(coords, props, u)
call expected_outputs(coords, props, u, expected_k, expected_rhs)
zeros12 = 0.0_dp
zeros_k = 0.0_dp
pnewdt = 0.5_dp
call call_adapter(2, rhs, amatrx, energy, pnewdt, status)
call assert_equal_int(status, UEL3DEB_OK, 'NOA-A-AMATRX-001 status LFLAGS3=2')
call assert_matrix_close(amatrx, expected_k, tol_stiff, 'NOA-A-AMATRX-001 K only')
call assert_vector_close(rhs(:, 1), zeros12, tol_vector, 'NOA-A-AMATRX-001 zero RHS')
call assert_close(pnewdt, 0.5_dp, tol_vector, 'NOA-A-ENERGY-001 PNEWDT K only')
pnewdt = 0.75_dp
call call_adapter(5, rhs, amatrx, energy, pnewdt, status)
call assert_equal_int(status, UEL3DEB_OK, 'NOA-A-RHS-001 status LFLAGS3=5')
call assert_matrix_close(amatrx, zeros_k, tol_stiff, 'NOA-A-RHS-001 zero AMATRX')
call assert_vector_close(rhs(:, 1), expected_rhs, tol_vector, 'NOA-A-RHS-001 RHS only')
call assert_close(pnewdt, 0.75_dp, tol_vector, 'NOA-A-ENERGY-001 PNEWDT RHS only')
end subroutine test_stiffness_only_and_residual_only
end program test_abi_static
@@ -0,0 +1,131 @@
program test_invalid_inputs
use ieee_arithmetic, only: ieee_quiet_nan, ieee_value
use uel_3d_euler_beam_test_support
use uel_3d_euler_beam_kernel, only: &
UEL3DEB_E001_NDOFEL, UEL3DEB_E002_NNODE, UEL3DEB_E003_MCRD, &
UEL3DEB_E004_NPROPS, UEL3DEB_E005_NJPROP, UEL3DEB_E006_NSVARS, &
UEL3DEB_E007_NRHS, UEL3DEB_E008_MLVARX, UEL3DEB_E009_NONFINITE, &
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_validate_kernel_inputs
use uel_3d_euler_beam_abi_adapter, only: uel3deb_abi_static
implicit none
call test_kernel_physical_diagnostics()
call test_adapter_shape_and_request_diagnostics()
write(*, '(A)') 'PASS uel_3d_euler_beam_invalid_inputs'
contains
subroutine valid_kernel_inputs(coords, props)
real(dp), intent(out) :: coords(3, 2)
real(dp), intent(out) :: props(9)
coords(:, 1) = [0.0_dp, 0.0_dp, 0.0_dp]
coords(:, 2) = [2.0_dp, 0.0_dp, 0.0_dp]
props = [210.0e9_dp, 80.0e9_dp, 3.0e-3_dp, 4.0e-6_dp, 7.0e-6_dp, &
2.5e-6_dp, 0.0_dp, 1.0_dp, 0.0_dp]
end subroutine valid_kernel_inputs
subroutine test_kernel_physical_diagnostics()
real(dp) :: coords(3, 2)
real(dp) :: props(9)
integer :: status
call valid_kernel_inputs(coords, props)
coords(1, 1) = ieee_value(coords(1, 1), ieee_quiet_nan)
call uel3deb_validate_kernel_inputs(coords, props, status)
call assert_equal_int(status, UEL3DEB_E009_NONFINITE, 'NOA-I-PHYS-001 nonfinite coordinate')
call valid_kernel_inputs(coords, props)
props(1) = -1.0_dp
call uel3deb_validate_kernel_inputs(coords, props, status)
call assert_equal_int(status, UEL3DEB_E010_NONPOSITIVE_PROPERTY, 'NOA-I-PHYS-001 nonpositive E')
call valid_kernel_inputs(coords, props)
coords(:, 2) = coords(:, 1)
call uel3deb_validate_kernel_inputs(coords, props, status)
call assert_equal_int(status, UEL3DEB_E011_ZERO_LENGTH, 'NOA-I-PHYS-001 zero length')
call valid_kernel_inputs(coords, props)
props(7:9) = [0.0_dp, 0.0_dp, 0.0_dp]
call uel3deb_validate_kernel_inputs(coords, props, status)
call assert_equal_int(status, UEL3DEB_E012_ZERO_ORIENTATION, 'NOA-I-PHYS-001 zero orientation')
call valid_kernel_inputs(coords, props)
props(7:9) = [1.0_dp, 0.0_dp, 0.0_dp]
call uel3deb_validate_kernel_inputs(coords, props, status)
call assert_equal_int(status, UEL3DEB_E013_PARALLEL_ORIENTATION, 'NOA-I-PHYS-001 parallel orientation')
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, &
UEL3DEB_E001_NDOFEL, 'NOA-I-SHAPE-001 NDOFEL')
call expect_adapter_status(12, 1, 0, 9, 3, 1, 12, 0, 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, &
UEL3DEB_E003_MCRD, 'NOA-I-SHAPE-001 MCRD')
call expect_adapter_status(12, 1, 0, 8, 3, 2, 12, 0, 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, &
UEL3DEB_E005_NJPROP, 'NOA-I-SHAPE-001 NJPROP')
call expect_adapter_status(12, 1, 1, 9, 3, 2, 12, 0, 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, &
UEL3DEB_E007_NRHS, 'NOA-I-SHAPE-001 NRHS')
call expect_adapter_status(12, 1, 0, 9, 3, 2, 11, 0, 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, &
UEL3DEB_E014_LFLAGS2, 'NOA-I-LFLAGS-001 LFLAGS2')
call expect_adapter_status(12, 1, 0, 9, 3, 2, 12, 0, 0, 0, 3, &
UEL3DEB_E015_LFLAGS3, 'NOA-I-LFLAGS-001 LFLAGS3')
call expect_adapter_status(12, 1, 0, 9, 3, 2, 12, 1, 0, 0, 1, &
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, &
expected_status, message)
integer, intent(in) :: ndofel
integer, intent(in) :: nrhs
integer, intent(in) :: nsvars
integer, intent(in) :: nprops
integer, intent(in) :: mcrd
integer, intent(in) :: nnode
integer, intent(in) :: mlvarx
integer, intent(in) :: ndload
integer, intent(in) :: njprop
integer, intent(in) :: lflag2
integer, intent(in) :: lflag3
integer, intent(in) :: expected_status
character(len=*), intent(in) :: message
real(dp) :: rhs(12, 2)
real(dp) :: amatrx(12, 12)
real(dp) :: energy(8)
real(dp) :: coords(3, 2)
real(dp) :: props(9)
real(dp) :: u(12)
real(dp) :: pnewdt
integer :: lflags(5)
integer :: jprops(1)
integer :: status
call valid_kernel_inputs(coords, props)
rhs = 0.0_dp
amatrx = 0.0_dp
energy = 0.0_dp
u = 0.0_dp
pnewdt = 1.0_dp
lflags = 0
lflags(2) = lflag2
lflags(3) = lflag3
jprops = 0
call uel3deb_abi_static(rhs, amatrx, energy, ndofel, nrhs, nsvars, props, &
nprops, coords, mcrd, nnode, u, lflags, mlvarx, &
ndload, pnewdt, jprops, njprop, status)
call assert_equal_int(status, expected_status, message)
end subroutine expect_adapter_status
end program test_invalid_inputs
@@ -0,0 +1,134 @@
program test_kernel_stiffness
use uel_3d_euler_beam_test_support
use uel_3d_euler_beam_kernel, only: &
UEL3DEB_OK, uel3deb_local_stiffness
implicit none
call test_local_stiffness_entries()
call test_analytical_responses()
write(*, '(A)') 'PASS uel_3d_euler_beam_kernel_stiffness'
contains
subroutine beam_values(length, young, shear, area, iy, iz, torsion_j)
real(dp), intent(out) :: length
real(dp), intent(out) :: young
real(dp), intent(out) :: shear
real(dp), intent(out) :: area
real(dp), intent(out) :: iy
real(dp), intent(out) :: iz
real(dp), intent(out) :: torsion_j
length = 2.0_dp
young = 210.0e9_dp
shear = 80.0e9_dp
area = 3.0e-3_dp
iy = 4.0e-6_dp
iz = 7.0e-6_dp
torsion_j = 2.5e-6_dp
end subroutine beam_values
subroutine build_local(k, length, young, shear, area, iy, iz, torsion_j)
real(dp), intent(out) :: k(12, 12)
real(dp), intent(out) :: length
real(dp), intent(out) :: young
real(dp), intent(out) :: shear
real(dp), intent(out) :: area
real(dp), intent(out) :: iy
real(dp), intent(out) :: iz
real(dp), intent(out) :: torsion_j
integer :: status
call beam_values(length, young, shear, area, iy, iz, torsion_j)
call uel3deb_local_stiffness(length, young, shear, area, iy, iz, torsion_j, k, status)
call assert_equal_int(status, UEL3DEB_OK, 'local stiffness status')
end subroutine build_local
subroutine test_local_stiffness_entries()
real(dp) :: k(12, 12)
real(dp) :: length, young, shear, area, iy, iz, torsion_j
real(dp) :: axial, torsion, b2_12, b2_6, b2_4, b2_2
real(dp) :: b3_12, b3_6, b3_4, b3_2
call build_local(k, length, young, shear, area, iy, iz, torsion_j)
axial = young * area / length
torsion = shear * torsion_j / length
b2_12 = 12.0_dp * young * iy / length**3
b2_6 = 6.0_dp * young * iy / length**2
b2_4 = 4.0_dp * young * iy / length
b2_2 = 2.0_dp * young * iy / length
b3_12 = 12.0_dp * young * iz / length**3
b3_6 = 6.0_dp * young * iz / length**2
b3_4 = 4.0_dp * young * iz / length
b3_2 = 2.0_dp * young * iz / length
call assert_matrix_symmetric(k, tol_symmetry, 'NOA-K-SYM-001 local symmetry')
call assert_close(k(1, 1), axial, tol_stiff, 'NOA-K-STIFF-001 axial k11')
call assert_close(k(1, 7), -axial, tol_stiff, 'NOA-K-STIFF-001 axial k17')
call assert_close(k(7, 7), axial, tol_stiff, 'NOA-K-STIFF-001 axial k77')
call assert_close(k(4, 4), torsion, tol_stiff, 'NOA-K-STIFF-001 torsion k44')
call assert_close(k(4, 10), -torsion, tol_stiff, 'NOA-K-STIFF-001 torsion k4,10')
call assert_close(k(10, 10), torsion, tol_stiff, 'NOA-K-STIFF-001 torsion k10,10')
call assert_close(k(3, 3), b2_12, tol_stiff, 'NOA-K-BEND2-001 k33')
call assert_close(k(3, 5), -b2_6, tol_stiff, 'NOA-K-BEND2-001 k35')
call assert_close(k(3, 9), -b2_12, tol_stiff, 'NOA-K-BEND2-001 k39')
call assert_close(k(3, 11), -b2_6, tol_stiff, 'NOA-K-BEND2-001 k3,11')
call assert_close(k(5, 5), b2_4, tol_stiff, 'NOA-K-BEND2-001 k55')
call assert_close(k(5, 9), b2_6, tol_stiff, 'NOA-K-BEND2-001 k59')
call assert_close(k(5, 11), b2_2, tol_stiff, 'NOA-K-BEND2-001 k5,11')
call assert_close(k(11, 11), b2_4, tol_stiff, 'NOA-K-BEND2-001 k11,11')
call assert_close(k(2, 2), b3_12, tol_stiff, 'NOA-K-BEND3-001 k22')
call assert_close(k(2, 6), b3_6, tol_stiff, 'NOA-K-BEND3-001 k26')
call assert_close(k(2, 8), -b3_12, tol_stiff, 'NOA-K-BEND3-001 k28')
call assert_close(k(2, 12), b3_6, tol_stiff, 'NOA-K-BEND3-001 k2,12')
call assert_close(k(6, 6), b3_4, tol_stiff, 'NOA-K-BEND3-001 k66')
call assert_close(k(6, 8), -b3_6, tol_stiff, 'NOA-K-BEND3-001 k68')
call assert_close(k(6, 12), b3_2, tol_stiff, 'NOA-K-BEND3-001 k6,12')
call assert_close(k(12, 12), b3_4, tol_stiff, 'NOA-K-BEND3-001 k12,12')
call assert_close(k(2, 3), 0.0_dp, tol_stiff, 'NOA-K-STIFF-001 uncoupled k23')
end subroutine test_local_stiffness_entries
subroutine test_analytical_responses()
real(dp) :: k(12, 12)
real(dp) :: force(12)
real(dp) :: u(12)
real(dp) :: length, young, shear, area, iy, iz, torsion_j
real(dp) :: axial, torsion, b2_12, b3_12
call build_local(k, length, young, shear, area, iy, iz, torsion_j)
axial = young * area / length
torsion = shear * torsion_j / length
b2_12 = 12.0_dp * young * iy / length**3
b3_12 = 12.0_dp * young * iz / length**3
u = 0.0_dp
u(7) = 1.0e-2_dp
force = matmul(k, u)
call assert_close(force(1), -axial * u(7), tol_vector, 'NOA-K-STIFF-002 f1')
call assert_close(force(7), axial * u(7), tol_vector, 'NOA-K-STIFF-002 f7')
u = 0.0_dp
u(10) = 2.0e-2_dp
force = matmul(k, u)
call assert_close(force(4), -torsion * u(10), tol_vector, 'NOA-K-STIFF-003 m1')
call assert_close(force(10), torsion * u(10), tol_vector, 'NOA-K-STIFF-003 m2')
u = 0.0_dp
u(9) = 1.0e-2_dp
force = matmul(k, u)
call assert_close(force(3), -b2_12 * u(9), tol_vector, 'NOA-K-BEND2-001 f3')
call assert_close(force(9), b2_12 * u(9), tol_vector, 'NOA-K-BEND2-001 f9')
u = 0.0_dp
u(8) = 1.0e-2_dp
force = matmul(k, u)
call assert_close(force(2), -b3_12 * u(8), tol_vector, 'NOA-K-BEND3-001 f2')
call assert_close(force(8), b3_12 * u(8), tol_vector, 'NOA-K-BEND3-001 f8')
end subroutine test_analytical_responses
end program test_kernel_stiffness
@@ -0,0 +1,168 @@
program test_kernel_transform_modes
use uel_3d_euler_beam_test_support
use uel_3d_euler_beam_kernel, only: &
UEL3DEB_OK, uel3deb_global_stiffness, uel3deb_local_stiffness
implicit none
call test_identity_and_rotated_transform()
call test_rigid_body_modes()
call test_reversed_node_order()
write(*, '(A)') 'PASS uel_3d_euler_beam_kernel_transform_modes'
contains
subroutine valid_props(props)
real(dp), intent(out) :: props(9)
props = [210.0e9_dp, 80.0e9_dp, 3.0e-3_dp, 4.0e-6_dp, 7.0e-6_dp, &
2.5e-6_dp, 0.0_dp, 1.0_dp, 0.0_dp]
end subroutine valid_props
subroutine fill_block_transform(rotation, transform)
real(dp), intent(in) :: rotation(3, 3)
real(dp), intent(out) :: transform(12, 12)
integer :: block
integer :: i
integer :: j
transform = 0.0_dp
do block = 0, 3
do i = 1, 3
do j = 1, 3
transform(3 * block + i, 3 * block + j) = rotation(i, j)
end do
end do
end do
end subroutine fill_block_transform
subroutine test_identity_and_rotated_transform()
real(dp) :: props(9)
real(dp) :: coords(3, 2)
real(dp) :: k_local(12, 12)
real(dp) :: k_global(12, 12)
real(dp) :: transform(12, 12)
real(dp) :: expected(12, 12)
real(dp) :: expected_transform(12, 12)
real(dp) :: rotation(3, 3)
integer :: status
call valid_props(props)
coords(:, 1) = [0.0_dp, 0.0_dp, 0.0_dp]
coords(:, 2) = [2.0_dp, 0.0_dp, 0.0_dp]
call uel3deb_local_stiffness(2.0_dp, props(1), props(2), props(3), props(4), &
props(5), props(6), k_local, status)
call assert_equal_int(status, UEL3DEB_OK, 'identity local stiffness status')
call uel3deb_global_stiffness(coords, props, k_global, transform, status)
call assert_equal_int(status, UEL3DEB_OK, 'identity global stiffness status')
call set_identity(expected_transform)
call assert_matrix_close(transform, expected_transform, tol_stiff, 'NOA-K-ROT-001 identity T')
call assert_matrix_close(k_global, k_local, tol_stiff, 'NOA-K-ROT-001 identity K')
props(7:9) = [0.0_dp, 0.0_dp, 1.0_dp]
coords(:, 2) = [0.0_dp, 2.0_dp, 0.0_dp]
call uel3deb_global_stiffness(coords, props, k_global, transform, status)
call assert_equal_int(status, UEL3DEB_OK, 'rotated global stiffness status')
rotation = 0.0_dp
rotation(1, 2) = 1.0_dp
rotation(2, 3) = 1.0_dp
rotation(3, 1) = 1.0_dp
call fill_block_transform(rotation, expected_transform)
expected = matmul(transpose(expected_transform), matmul(k_local, expected_transform))
call assert_matrix_close(transform, expected_transform, tol_stiff, 'NOA-K-ROT-001 rotated T')
call assert_matrix_close(k_global, expected, tol_stiff, 'NOA-K-ROT-001 rotated K')
call assert_matrix_symmetric(k_global, tol_symmetry, 'NOA-K-SYM-001 global symmetry')
end subroutine test_identity_and_rotated_transform
subroutine test_rigid_body_modes()
real(dp) :: props(9)
real(dp) :: coords(3, 2)
real(dp) :: k_global(12, 12)
real(dp) :: transform(12, 12)
real(dp) :: mode(12)
integer :: status
call valid_props(props)
coords(:, 1) = [0.0_dp, 0.0_dp, 0.0_dp]
coords(:, 2) = [2.0_dp, 0.0_dp, 0.0_dp]
call uel3deb_global_stiffness(coords, props, k_global, transform, status)
call assert_equal_int(status, UEL3DEB_OK, 'rigid mode stiffness status')
mode = 0.0_dp
mode([1, 7]) = 1.0_dp
call assert_rigid_mode(k_global, mode, 'NOA-K-RBM-001 rigid U1')
mode = 0.0_dp
mode([2, 8]) = 1.0_dp
call assert_rigid_mode(k_global, mode, 'NOA-K-RBM-001 rigid U2')
mode = 0.0_dp
mode([3, 9]) = 1.0_dp
call assert_rigid_mode(k_global, mode, 'NOA-K-RBM-001 rigid U3')
mode = 0.0_dp
mode([4, 10]) = 1.0_dp
call assert_rigid_mode(k_global, mode, 'NOA-K-RBM-001 rigid UR1')
mode = 0.0_dp
mode(3) = 0.0_dp
mode(5) = 1.0_dp
mode(9) = -2.0_dp
mode(11) = 1.0_dp
call assert_rigid_mode(k_global, mode, 'NOA-K-RBM-001 rigid UR2')
mode = 0.0_dp
mode(2) = 0.0_dp
mode(6) = 1.0_dp
mode(8) = 2.0_dp
mode(12) = 1.0_dp
call assert_rigid_mode(k_global, mode, 'NOA-K-RBM-001 rigid UR3')
end subroutine test_rigid_body_modes
subroutine assert_rigid_mode(k_global, mode, message)
real(dp), intent(in) :: k_global(12, 12)
real(dp), intent(in) :: mode(12)
character(len=*), intent(in) :: message
real(dp) :: residual(12)
real(dp) :: limit
residual = matmul(k_global, mode)
limit = tol_vector * max(1.0_dp, max_abs_matrix(k_global) * max_abs_vector(mode))
call assert_vector_norm_le(residual, limit, message)
end subroutine assert_rigid_mode
subroutine test_reversed_node_order()
real(dp) :: props(9)
real(dp) :: coords_forward(3, 2)
real(dp) :: coords_reversed(3, 2)
real(dp) :: k_forward(12, 12)
real(dp) :: k_reversed(12, 12)
real(dp) :: transform(12, 12)
real(dp) :: expected_reversed(12, 12)
integer :: status
integer :: i
integer :: j
integer :: permutation(12)
call valid_props(props)
coords_forward(:, 1) = [0.0_dp, 0.0_dp, 0.0_dp]
coords_forward(:, 2) = [2.0_dp, 0.0_dp, 0.0_dp]
coords_reversed(:, 1) = coords_forward(:, 2)
coords_reversed(:, 2) = coords_forward(:, 1)
call uel3deb_global_stiffness(coords_forward, props, k_forward, transform, status)
call assert_equal_int(status, UEL3DEB_OK, 'forward node stiffness status')
call uel3deb_global_stiffness(coords_reversed, props, k_reversed, transform, status)
call assert_equal_int(status, UEL3DEB_OK, 'reversed node stiffness status')
permutation = [7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6]
do i = 1, 12
do j = 1, 12
expected_reversed(i, j) = k_forward(permutation(i), permutation(j))
end do
end do
call assert_matrix_close(k_reversed, expected_reversed, tol_stiff, 'NOA-K-REVNODE-001')
end subroutine test_reversed_node_order
end program test_kernel_transform_modes
@@ -0,0 +1,138 @@
module uel_3d_euler_beam_test_support
use iso_fortran_env, only: real64
implicit none
integer, parameter :: dp = real64
real(dp), parameter :: tol_stiff = 1.0e-10_dp
real(dp), parameter :: tol_vector = 1.0e-10_dp
real(dp), parameter :: tol_symmetry = 1.0e-12_dp
contains
subroutine fail_test(message)
character(len=*), intent(in) :: message
write(*, '(A)') 'FAIL: ' // trim(message)
error stop 1
end subroutine fail_test
subroutine assert_true(condition, message)
logical, intent(in) :: condition
character(len=*), intent(in) :: message
if (.not. condition) call fail_test(message)
end subroutine assert_true
subroutine assert_equal_int(actual, expected, message)
integer, intent(in) :: actual
integer, intent(in) :: expected
character(len=*), intent(in) :: message
if (actual /= expected) then
write(*, *) 'actual=', actual, ' expected=', expected
call fail_test(message)
end if
end subroutine assert_equal_int
subroutine assert_close(actual, expected, tolerance, message)
real(dp), intent(in) :: actual
real(dp), intent(in) :: expected
real(dp), intent(in) :: tolerance
character(len=*), intent(in) :: message
real(dp) :: limit
limit = tolerance * max(1.0_dp, abs(expected))
if (abs(actual - expected) > limit) then
write(*, *) 'actual=', actual, ' expected=', expected, ' limit=', limit
call fail_test(message)
end if
end subroutine assert_close
function max_abs_matrix(values) result(max_abs)
real(dp), intent(in) :: values(:, :)
real(dp) :: max_abs
max_abs = maxval(abs(values))
end function max_abs_matrix
function max_abs_vector(values) result(max_abs)
real(dp), intent(in) :: values(:)
real(dp) :: max_abs
max_abs = maxval(abs(values))
end function max_abs_vector
subroutine assert_matrix_close(actual, expected, tolerance, message)
real(dp), intent(in) :: actual(:, :)
real(dp), intent(in) :: expected(:, :)
real(dp), intent(in) :: tolerance
character(len=*), intent(in) :: message
real(dp) :: diff
real(dp) :: limit
call assert_true(all(shape(actual) == shape(expected)), trim(message) // ' shape')
diff = maxval(abs(actual - expected))
limit = tolerance * max(1.0_dp, max_abs_matrix(expected))
if (diff > limit) then
write(*, *) 'max matrix diff=', diff, ' limit=', limit
call fail_test(message)
end if
end subroutine assert_matrix_close
subroutine assert_vector_close(actual, expected, tolerance, message)
real(dp), intent(in) :: actual(:)
real(dp), intent(in) :: expected(:)
real(dp), intent(in) :: tolerance
character(len=*), intent(in) :: message
real(dp) :: diff
real(dp) :: limit
call assert_true(size(actual) == size(expected), trim(message) // ' size')
diff = maxval(abs(actual - expected))
limit = tolerance * max(1.0_dp, max_abs_vector(expected))
if (diff > limit) then
write(*, *) 'max vector diff=', diff, ' limit=', limit
call fail_test(message)
end if
end subroutine assert_vector_close
subroutine assert_vector_norm_le(actual, limit, message)
real(dp), intent(in) :: actual(:)
real(dp), intent(in) :: limit
character(len=*), intent(in) :: message
real(dp) :: norm_inf
norm_inf = max_abs_vector(actual)
if (norm_inf > limit) then
write(*, *) 'max vector norm=', norm_inf, ' limit=', limit
call fail_test(message)
end if
end subroutine assert_vector_norm_le
subroutine assert_matrix_symmetric(values, tolerance, message)
real(dp), intent(in) :: values(:, :)
real(dp), intent(in) :: tolerance
character(len=*), intent(in) :: message
real(dp) :: diff
real(dp) :: limit
call assert_true(size(values, 1) == size(values, 2), trim(message) // ' square')
diff = maxval(abs(values - transpose(values)))
limit = tolerance * max(1.0_dp, max_abs_matrix(values))
if (diff > limit) then
write(*, *) 'symmetry diff=', diff, ' limit=', limit
call fail_test(message)
end if
end subroutine assert_matrix_symmetric
subroutine set_identity(values)
real(dp), intent(out) :: values(:, :)
integer :: i
values = 0.0_dp
do i = 1, min(size(values, 1), size(values, 2))
values(i, i) = 1.0_dp
end do
end subroutine set_identity
end module uel_3d_euler_beam_test_support