Files
FESADev/docs/hht-implicit-dynamics/io-contract.md
T
2026-10-04 03:32:59 +09:00

154 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HHT I/O 계약
- 상태: `io-contract-defined; implementation-and-readback-pending`, 2026-09-22.
- Owner: `io-definition-agent`. 메인 인계로 새 procedure의 아래 공개 subset/schema를 확정한다. 물리·수치 기준의 원본은 [formulation](formulation.md), [inertia](formulation-inertia.md), [numerical-controls](numerical-controls.md)다.
- 기준: [AGENTS](../../AGENTS.md), [프로젝트 PRD](../PRD.md), [공통 ADR](../ADR.md), [ARCHITECTURE](../ARCHITECTURE.md), [Feature PRD](PRD.md), [Feature ADR](ADR.md). 기존 문서의 `I/O pending`은 본 계약 이전 상태다.
## 1. 입력과 호환성
기존 [비선형 I/O](../nonlinear-static-newton-raphson/io.md)의 lexical parsing, source target resolution, part/identity instance, node/set, S4/S4R → FESA-MITC4, centered homogeneous shell, isotropic elastic material, geometry와 chart 검증을 상속한다. 아래 절차 차이만 dynamic에 적용한다. 기존 static/frequency 입력·결과의 의미나 schema는 바꾸지 않는다. Source S4/S4R 표기는 보존하며 Abaqus 내부 요소·관성·시간 알고리즘 동등성을 주장하지 않는다. 공개 Abaqus 관례의 근거는 이미 조사한 [research](research.md#research)의 관측 가능한 입력 의미에 한정한다.
| 구문 | 이번 지원 의미 / 검증 |
| --- | --- |
| `*STEP, NAME=<name>, NLGEOM=YES, INC=<integer>` | 한 step만 허용. NAME은 기존 규칙, 생략 시 `Step-1`; NLGEOM=YES 필수. INC 생략값 100, 명시값 양의 uint32. INC는 시간 increment 상한이며 Newton 반복 상한이 아님 |
| `*DYNAMIC, DIRECT` | step의 첫 procedure. DIRECT는 값 없는 flag, 다른 parameter는 거부. ALPHA/BETA/GAMMA, APPLICATION, INITIAL, EXPLICIT, 자동 증분은 미지원 |
| data `h,T` 또는 `h,T,` | 정확히 한 row, 두 finite positive 값. h≤T. `N=round(T/h)`가 uint32 범위의 양의 정수이고 `double(N)*h == T`, N≤INC일 때만 허용. 중간 누적 덧셈·끝 증분 단축·time tolerance 없음. 그 밖의 nonempty field/row 거부 |
| `*DENSITY` 아래 `rho` 또는 `rho,` | 활성 material마다 정확히 한 finite positive density; temperature/dependency/추가 field 미지원. 질량 단위는 `force*time^2/length`, density는 `force*time^2/length^4`. Frequency의 기존 허용 유지, static에 새 허용하지 않음 |
| `*BOUNDARY` | 모델 앞 또는 procedure 뒤/하중 앞. 기존 target, DOF 1–6 범위; magnitude 생략 또는 exact 0. 전체 시간에 q/v/a/A constrained 성분 exact 0. 초기 q/v와 충돌하는 값, AMPLITUDE/OP/TYPE/변경 경계 거부 |
| partial zero BC | component selector 의미를 그대로 보존. 전 DOF clamp/free 필수 지원, 부분구속은 N4 analytic null과 free 공간 교집합 사용. Rank-changing branch/regularity 실패는 오류이며 부분구속을 full clamp로 바꾸지 않음 |
| `*CLOAD` | step 내 global dead force DOF 1–3만 허용. finite magnitude; 동일 resolved node/DOF의 여러 row는 source order로 합산. DOF 4–6은 값이 0이어도 미지원. AMPLITUDE/FOLLOWER/OP 및 모델-level 하중 미지원 |
| `*PREPRINT, ECHO=NO, MODEL=NO, HISTORY=NO, CONTACT=NO` | 정확한 flags, data 없음: `ignored-output-request` warning. 출력 제어 효과 없음 |
| `*RESTART, WRITE, FREQUENCY=0` | data 없는 이 조합만 warning no-op. Restart 생성·읽기 지원 아님 |
| `*OUTPUT, FIELD, VARIABLE=PRESELECT, FREQUENCY=1` | data 없음. HISTORY도 같은 조합 허용, FIELD/HISTORY 둘 중 하나. FREQUENCY 생략도 1 의미로 수용. 요청마다 warning no-op; FESA 필수 전 frame 출력을 바꾸지 않음 |
`*NODE OUTPUT`, `*ELEMENT OUTPUT` 등 child selectors와 다른 OUTPUT parameter/frequency는 미지원이다. 원본 [INP](../../reference/shellNL-dyn-meshfine/shellNL-dyn-meshfine.inp)는 위 root 요청만 사용한다. 초기조건 keyword, amplitude, damping, distributed/follower load, contact, 비탄성, 다른 dynamic 요소, 다중 step/restart는 fail closed다. 기존 shell section의 source integration-point field `5`는 기존 allowlisted warning 의미를 유지하며 FESA 관성 적분 선택이 아니다.
공개 계수는 기본 `alpha=-0.05,beta=0.275625,gamma=0.55`만 제공한다. Internal 검증용 alpha=0은 formulation의 결합계수로 실행할 수 있으나 INP 지원 확대가 아니다. 질량은 `consistent-kinetic`; 사용자 선택을 lumped fallback으로 바꾸지 않는다. 초기 frame은 `0+`, q0=v0=0, F0=F*, A0=a0이며 physical a0와 null 일관성은 N3에 따라 구한다. 하중은 이후 모든 endpoint에도 전값이다.
## 2. Identity, 시간, state 경계
Canonical storage slot은 `/steps/Step-1`이다. 실제 source name을 별도 `source_step_name`에 저장한다. Reference의 실제 name은 `Step-1`이다. Instance는 기존 ASCII case-insensitive canonical identity와 raw label text를 보존하고, logical label은 positive int64다. Internal node/element ID와 equation ID를 source label 대신 사용하지 않는다.
N increments의 완성 결과는 frame 0..N, increment 0..N을 각각 한 번 포함한다. Frame n의 `time=double(n)*h`, frame0=+0, `is_initial=(n==0)`이며 n≥1에서 시간이 strictly increasing해야 한다. 종료시각은 T와 exact 일치한다. Frame0 iteration_count=0; 이후 0..100 corrections, 초기 추정 수렴/empty free solve는 0 허용. INC에 frame0는 포함하지 않는다. Original h=.1,T=10,INC=1000은 N=100/101 frames다.
Domain이 dynamic semantic 정의를 소유한다. AnalysisModel은 non-owning view, DofManager는 full/free/constraint mapping을 소유한다. State vector의 node-major 6성분은 `[u1,u2,u3,phi1,phi2,phi3]`이며 node inventory는 Domain 선언 순서다. Rotation은 fixed-initial global total rotation-vector chart다. Trial은 committed state/history와 alias되지 않는다. Candidate의 time, q/v/a/A, orientation, force, recovery와 evidence가 모두 검증된 뒤 한 번에 commit한다. Failure는 마지막 committed frame과 history를 보존하며 failed/trial frame을 노출하지 않는다.
### 구현 인계용 C++ 경계
이는 backend-neutral 경계이며 vendor type을 포함하지 않는다. Default-constructed record는 구성 중 candidate일 뿐 유효한 완료 state가 아니다.
```cpp
struct DynamicStepDefinition {
std::string source_step_name_raw, source_step_name_canonical;
SourceLocation step_location, dynamic_location, data_location;
double initial_increment = 0.0, time_period = 0.0;
std::uint32_t increment_count = 0, maximum_increments = 100;
double alpha = -0.05, beta = 0.275625, gamma = 0.55;
std::vector<PrescribedDisplacementDefinition> boundaries;
std::vector<NodalLoad> loads;
};
struct DynamicAcceptedFrameState {
std::uint32_t frame_index = 0, increment_index = 0, iteration_count = 0;
double time = 0.0;
bool is_initial = false;
std::vector<EntityIndex> nodal_internal_order;
std::vector<SourceEntityId> nodal_source_ids;
Vector generalized_coordinates, velocity, acceleration;
Vector algorithmic_acceleration; // Each 6*N; physical a != auxiliary A.
std::vector<std::array<std::array<double, 3>, 3>> nodal_triad;
std::vector<std::array<double, 3>> nodal_director;
Vector angular_velocity, angular_acceleration; // Each 3*N, spatial.
Vector inertia_force, internal_force, external_force, residual, reaction;
Vector residual_scale; // Each 6*N, N2 uncancelled per-row scale.
std::vector<ShellResultRow> shell_results;
double kinetic_energy = 0.0, physical_strain_energy = 0.0;
double drilling_energy = 0.0, external_work = 0.0, energy_defect = 0.0;
std::array<double, 6> global_equilibrium{};
std::array<double, 3> linear_momentum{}, angular_momentum{};
std::array<double, 3> linear_momentum_rate{}, angular_momentum_rate{};
std::array<double, 2> free_residual_metrics{}; // u, phi channels.
Vector hidden_constraint, hidden_velocity_defect, hidden_acceleration_defect;
Vector hidden_constraint_scale, hidden_velocity_defect_scale;
Vector hidden_acceleration_defect_scale; // Each analytic null count nz.
};
```
`ModelDefinition.dynamic_step:optional<DynamicStepDefinition>`, `StepDefinition::IsDynamic()`, `StepDefinition::Dynamic()` 및 `Domain::Dynamic()` accessor를 추가한다. Dynamic과 static/frequency 정의는 상호배타적이다. `DynamicAnalysisState::Create(const Domain&,const DofManager&):Result<DynamicAnalysisState>`는 immutable inventory/schedule을 검증한다. 기본 empty 구성이 가능하되 Create 전 commit/실행/Write를 거부한다. `BeginTrialFromCommitted():Result<DynamicAcceptedFrameState>`는 deep copy, `CommitAcceptedFrame(const DynamicAcceptedFrameState&):Status`는 강한 실패 보존, `DiscardTrial():void`는 caller-owned trial 폐기를 위한 no-op이다. `AcceptedFrames():const vector<DynamicAcceptedFrameState>&`, `CommittedState():const DynamicAcceptedFrameState&`를 제공한다. Frame0는 Create 후 완성하여 같은 commit 검사를 통과해야 한다. 첫 commit 전 CommittedState 읽기는 금지다. Full q6N이 authority이며 displacement/rotation state를 중복 소유하지 않는다.
`DynamicExecutionResult { DynamicAnalysisState state; vector<Diagnostic> diagnostics; }`를 PreparedAnalysisOutcome에 추가하고 별도 dynamic executor를 dispatch한다. `DynamicResultsWriter::Write(const filesystem::path&, const Domain&, const DynamicAnalysisState&, const vector<Diagnostic>&):Status` 및 `DynamicHdf5ResultsWriter`를 추가한다. Existing static writer에 time 또는 static load_factor로 dynamic을 위장하지 않는다.
Momentum/rates는 전체 두께 volume kinetic kinematics로 구한 physical quantities, 원점 O=(0,0,0)에 대한 global angular momentum이다. Chart generalized momentum을 spatial angular momentum으로 표시하지 않는다. `global_equilibrium`은 N6의 applied+support minus momentum-rate이며 current arms와 spatial moments를 사용한다. Hidden vectors는 N3의 raw ψ/1차/2차 defects, `_scale`는 그 항별 uncancelled norm scale다. n_z=0은 empty vector, empty free channel metric 저장값 0은 해당 constraint inventory와 함께 N/A로 해석한다. NaN sentinel을 사용하지 않는다. Hidden 1·2차 defect는 진단/시간수렴 대상이며 Newton 수렴 기준으로 임의 승격하지 않는다.
## 3. 공식 HDF5 schema v1
공식 경로는 input lexical sibling `<stem>_results.h5`, CLI/lock/log/atomic file lifecycle은 기존 I/O §7.1을 상속한다. Dynamic log는 `nsnr-log-v1` 또는 target_factor를 사용하지 않는다. 기존 log stream에 dynamic diagnostic을 기록할 경우 `record=dynamic-newton-iteration`, `schema=hht-log-v1`, increment/time/iteration/stage와 N2 metric의 raw numerator/scale/branch/pass를 명시한다. 정적 전용 tolerance를 재사용하지 않는다.
Schema identity는 `(feature_id,analysis_procedure,schema_version)`다. `/metadata` attribute: `feature_id=hht-implicit-dynamics`, `analysis_procedure=hht-implicit-dynamics`, `schema_version:uint64=1`, `mass_formulation=consistent-kinetic`, `internal_formulation=FESA-MITC4-total-lagrangian`, `initial_state=zero-q-v-step-load-0+`, `acceleration_convention=physical-endpoint-and-auxiliary-separated`, `source_step_name`, `step_name=Step-1`, `alpha/beta/gamma:float64`, `initial_increment/time_period:float64`, `increment_count/maximum_increments:uint64`. 기존 solver_version/source_input_identity/unit_system_label도 필수, 단위 label은 `consistent-input-units`이며 SI로 추정하지 않는다.
모든 numeric data는 finite-first, float64=IEEE binary64 little-endian, integer는 fixed width little-endian, string은 UTF-8 variable length다. Mandatory datasets는 contiguous/uncompressed. 아래 N=nodes, E=elements, F=Nincrements+1이다. Paths의 `<n>`은 leading-zero 없는 decimal ordinal이다.
Model schema는 기존 I/O §7.3의 `/model/nodes`, `/model/elements`, `/model/shell/materials`, `/model/shell/sections`, `/model/shell/nodal_director`, `/model/shell/nodal_frame`, `/model/nodal_constraint_mask`, `/model/shell/midsurface_locations`, `/model/shell/section_positions`의 정확한 dtype/shape/identity를 재사용한다. `/model/prescribed_displacement_start/target`은 dynamic에서 쓰지 않는다. 추가 `/model/prescribed_displacement:[N,6] float64`는 전값 0, `/model/material_density:[material_count]` compound는 `internal_material_id:uint64,rho:float64`로 materials 행 순서다. 활성 material의 rho는 finite positive다.
`/steps/Step-1/accepted_frames:[F]` compound fields는 `frame_index:uint64,increment_index:uint64,time:float64,iteration_count:uint64,is_initial:uint8`. Table의 시간과 모든 frame attrs가 일치해야 한다. Load factor는 없다.
각 `/steps/Step-1/frames/<n>` 아래 필수 dataset은 다음과 같다.
| 상대 path | shape, dtype | component / 의미 / 차원 |
| --- | --- | --- |
| `nodal/displacement` | [N,6] float64 | UX,UY,UZ,URX,URY,URZ; q; length×3,1×3 (UR는 rad) |
| `nodal/velocity` | [N,6] float64 | V1,V2,V3,PHIDOT1,PHIDOT2,PHIDOT3; chart v; length/time×3,1/time×3 |
| `nodal/acceleration` | [N,6] float64 | A1,A2,A3,PHIDDOT1,PHIDDOT2,PHIDDOT3; physical chart a; length/time²×3,1/time²×3 |
| `nodal/algorithmic_acceleration` | [N,6] float64 | AUX1,AUX2,AUX3,AUX4,AUX5,AUX6; auxiliary A; a와 같은 차원 |
| `nodal/angular_velocity` | [N,3] float64 | OMEGA1,OMEGA2,OMEGA3; J_l(phi) phidot; 1/time |
| `nodal/angular_acceleration` | [N,3] float64 | OMEGADOT1,OMEGADOT2,OMEGADOT3; J_l phiddot+DJ_l[phidot]phidot; 1/time² |
| `nodal/triad`, `nodal/director` | [N,3,3], [N,3] float64 | 기존 current triad/director 의미, axis row A,B,D / global XYZ |
| `nodal/generalized_inertia_force` | [N,6] float64 | I=M(q)a+c(q,v), physical endpoint |
| `nodal/generalized_internal_force` | [N,6] float64 | physical+objective drilling internal force |
| `nodal/generalized_external_force` | [N,6] float64 | F*, frame0에도 full value |
| `nodal/generalized_residual` | [N,6] float64 | E=I+f_int-F, free와 constrained 전체 |
| `nodal/generalized_reaction` | [N,6] float64 | constrained selector를 E에 적용, free 성분 exact zero |
| `nodal/reaction` | [N,6] float64 | RF1,RF2,RF3,RM1,RM2,RM3; support-on-structure; moment=J_l^-T generalized reaction |
| `element/shell/local_frame` | [E,4,3,3] float64 | 기존 GP1..4, local axes E1,E2,E3 |
| `element/shell/generalized_strain` | [E,4,8] float64 | 기존 E11,E22,G12,K11,K22,K12,G13,G23 |
| `element/shell/section_resultant` | [E,4,8] float64 | 기존 N11,N22,N12,M11,M22,M12,Q13,Q23 |
| `element/shell/stress` | [E,4,3,3] float64 | 기존 BOTTOM,MIDDLE,TOP × S11,S22,S12 |
| `global/energy` | [5] float64 | KINETIC_ENERGY,PHYSICAL_STRAIN_ENERGY,DRILLING_ENERGY,EXTERNAL_WORK,ENERGY_DEFECT; 모두 force*length |
| `global/equilibrium` | [6] float64 | FORCE_1..3,MOMENT_1..3; N6 관성 포함 balance error |
Generalized force 계열 component_names=`F1,F2,F3,MPHI1,MPHI2,MPHI3`, units=`force,force,force,force*length,force*length,force*length`, coordinate_system=`fixed-initial-global-chart`. Spatial U/omega/reaction은 `global-cartesian`; q/v/a/A는 translation global 및 rotation chart임을 `rotation_representation=total-rotation-vector-chart`와 `quantity_semantics`로 명시한다. Shell units, local_frame/source_element_type/midsurface/section_position 참조 attrs는 기존 I/O §7.5 그대로다. Numerical drill force를 physical shell stress/resultant에 섞지 않는다.
모든 result dataset에 `schema_version`, `component_names`, `component_unit_dimensions`, `coordinate_system`, `location`, `step_name`, `source_step_name`, `frame_index`, `increment_index`, `time`, `is_initial` attr를 쓴다. Scalar numeric attrs는 위 dtype, flags uint8이다. Location은 `nodal`, shell 기존 location, `global`/`global-balance-error`다. 모든 nodal row는 `/model/nodes`, element row는 `/model/elements` 행을 참조한다.
Energy의 W=F*ᵀ(qn-q0), defect=T+Uphys+Udrill-E0-W다. Initial physical energy E0는 frame0의 첫 세 성분 합이며 W0/defect0=0. A를 kinetic energy나 physical inertia에 쓰지 않는다. Algorithmic dissipation을 physical damping energy로 이름 붙이지 않는다. Raw momentum/hidden diagnostics는 위 internal frame의 필수 evidence이며 이번 persistent schema에는 별도 필수 dataset을 추가하지 않는다. HDF5에서 계산할 수 없는 상세 검증은 실행 보고서가 internal evidence를 기록한다.
`/diagnostics`는 기존 diagnostic의 의미·dtype·정렬을 재사용하되 dynamic compound field 이름을 severity/code/source_file/source_line/keyword/entity_identity/message로 명시한다. 기존 static writer의 file/line 이름은 이 dynamic schema에 적용하지 않는다. Writer는 전체 frames/node/element inventory, dtype/rank/component/attrs/time/finite/constraint, complete final frame를 검사하고 write/flush/close/reopen/self-check 후 원자 replace한다. 기존 final file은 실패 시 보존한다. Live log의 이미 flush된 prefix는 rollback하지 않는다. No-op warning이 있어도 필수 dataset을 생략할 수 없다.
## 4. 진단과 비교 binding
기존 `Diagnostic`의 severity/code/location/keyword/entity_identity/message를 사용하며 `SortDiagnostics` 순서 유지. 위치는 가능한 source keyword/data row, runtime에서는 step+increment와 node/element source identity를 message/entity_identity에 포함한다. Parser/model/output/backend의 기존 FailureCategory/CLI exit mapping을 유지하고 새 exit code를 만들지 않는다.
| code | 발생 조건 |
| --- | --- |
| `unsupported-dynamic-parameter` | DIRECT 외 dynamic parameter, 잘못된 flag, 계수 입력 |
| `invalid-dynamic-data` | h/T/nonfinite/arity/범위 오류 |
| `invalid-dynamic-schedule` | N*h≠T, N>INC, uint32 초과, 시간 비증가 |
| `unsupported-dynamic-boundary` | 비영/시간변화/BC option |
| `unsupported-dynamic-load` | CLOAD4–6, follower/amplitude/distributed load |
| `invalid-density-data` | 누락/중복/nonpositive/비상수 density; 기존 code 재사용 |
| `unsupported-analysis-procedure`, `unsupported-multiple-step`, `unsupported-keyword` | 잘못된 procedure/NLGEOM, 다중 step, allowlist 밖 의미 |
| `ignored-output-request` | 위 정확한 allowlisted no-op만 warning |
| `dynamic-initial-consistency`, `dynamic-mass-regularity` | N3 초기화/숨은 조건 또는 N4 rank/positive block/regularity 실패 |
| `dynamic-nonconvergence`, `dynamic-linear-solve` | N2 correction 한도 또는 solve/backward-error 실패 |
| `dynamic-invalid-state`, `dynamic-output-schema` | chart/geometry/nonfinite/inventory/update 또는 저장/재독 계약 위반 |
기존 geometry/backend code가 더 구체적이면 보존하며 위 code로 감싸 원래 source context를 버리지 않는다. 질량 singularity 자체는 오류가 아니다. Formulation에 정의된 허용 null을 제거하거나 epsilon mass를 주지 않는다.
[Reference 계약](reference-model.md)의 source step/instance/node/increment/time/component authority를 그대로 적용한다. Binding: `/model/nodes`의 `(instance_name,source_label)`와 frame table을 조인하고 `nodal/displacement[:,0:3]→U-U1/U2/U3`, `[:,3:6]→UR-UR1/UR2/UR3`. 먼저 전체 729-node 101-frame identity/finite 검사를 수행하고 고정 9-node subset으로 projection한다. Decimal CSV time=n/10 검증과 binary HDF5 time=double(n)*double(.1) 검증을 각각 수행한다. Nearest-time/보간/평균/row-index matching은 금지다. Wrong procedure/mass/coefficient/초기 convention, missing/extra/duplicate/nonfinite는 tolerance 전에 실패다. Frame별 U blocking/UR warning-only 202 families와 공통 [TOLERANCE](../TOLERANCE.md)는 변경하지 않는다.
## 5. 인계 및 확인 상태
입력 원본의 procedure/density/BC/CLOAD/output 요청과 기존 semantic/prepared_analysis/state/writer 경계를 대조했다. 이번 변경은 이 문서뿐이며 source/test/reference artifact는 수정하지 않았다. 문서 링크와 diff를 정적 검사한다. Build/test·dynamic HDF5 readback·reference 비교는 문서 owner의 작업이 아니므로 미실행이다.
상위 물리 선택 충돌은 없다. 구현에 아직 필요한 upstream quantity는 별도 Udrill energy, exact inertia/kinetic energy, spatial rates, physical momentum/rates, N2 uncancelled per-row scale 및 N3 hidden defects다. 기존 nonlinear recovery가 Uphys만 반환한다는 이유로 Udrill=0 또는 누락으로 바꾸면 안 된다. 메인은 이 문서를 main report에서 연결하고 공개 subset/schema 결정 근거를 Feature ADR에 기록한다. 이 계약 정의만으로 solver/physics gate가 통과한 것은 아니다.