feat(thermal): assemble heat operators and preserve typed frame history

This commit is contained in:
KOKO\Mimi
2026-09-29 20:09:13 +09:00
parent 85b5daac89
commit 54154bee15
7 changed files with 1462 additions and 0 deletions
@@ -0,0 +1,95 @@
#ifndef FESA_ANALYSIS_THERMAL_ANALYSIS_STATE_H_
#define FESA_ANALYSIS_THERMAL_ANALYSIS_STATE_H_
#include <array>
#include <cstdint>
#include <vector>
#include "fesa/analysis/thermal_time_grid.h"
#include "fesa/assembly/thermal_assembler.h"
#include "fesa/results/thermal_result_records.h"
namespace fesa {
/// @brief Owns the immutable thermal result inventory and accepted history.
/// @note No Domain/model/assembly references survive Create; state may outlive
/// them. Numerical equilibrium and energy checks belong to the procedure before
/// Commit.
class ThermalAnalysisState final {
public:
/// @brief Validates the inventory/schedule and commits raw initial
/// temperature.
/// @note Initial frame has no recovery payload and is never projected onto
/// BCs.
static Result<ThermalAnalysisState> Create(const AnalysisModel& model,
const DofManager& dofs,
const ThermalAssembly& assembly);
/// @brief Builds a complete typed candidate without changing accepted
/// history.
/// @param temperature Full node-major temperature, including prescribed
/// values.
/// @param reaction Constrained full residual in ReactionDofs() order, even if
/// empty.
/// @param flux Global element fluxes in ElementOrder(), point/trace order.
Result<ThermalAcceptedFrame> MakeCompletedFrame(
std::uint32_t index, Vector temperature, Vector reaction,
std::vector<ThermalElementFlux> flux) const;
/// @brief Atomically appends the next finite frame after exact
/// mapping/schedule checks.
/// @note Rejection preserves every existing frame; caller-owned candidates
/// stay independent.
Status CommitAcceptedFrame(const ThermalAcceptedFrame& frame);
/// @brief Returns accepted frames, beginning with raw frame zero.
const std::vector<ThermalAcceptedFrame>& AcceptedFrames() const noexcept;
/// @brief Returns the most recently committed state; Create always supplies
/// frame zero.
const ThermalAcceptedFrame& CommittedState() const noexcept;
/// @brief Reports whether every scheduled increment has been committed.
bool IsComplete() const noexcept;
/// @brief Returns the exact time-grid schedule used to validate accepted
/// frames.
const ThermalTimeGrid& TimeGrid() const noexcept;
/// @brief Returns original nominal dt for metadata, independent of actual
/// last delta.
double NominalTimeIncrement() const noexcept;
/// @brief Returns the exact input end time for metadata.
double TimePeriod() const noexcept;
/// @brief Returns active Domain node IDs in temperature row order.
const std::vector<EntityIndex>& NodeOrder() const noexcept;
/// @brief Returns immutable source identities corresponding to NodeOrder().
const std::vector<SourceEntityId>& NodeSourceIds() const noexcept;
/// @brief Returns Domain element IDs in flux/frame row order.
const std::vector<EntityIndex>& ElementOrder() const noexcept;
/// @brief Returns immutable source identities corresponding to
/// ElementOrder().
const std::vector<SourceEntityId>& ElementSourceIds() const noexcept;
/// @brief Returns the N by 5 free=0/prescribed=1 mask.
const std::vector<std::array<std::uint8_t, 5>>& ConstraintMask()
const noexcept;
/// @brief Returns prescribed node/source-DOF identities in node/station
/// order.
const std::vector<ThermalReactionDof>& ReactionDofs() const noexcept;
/// @brief Returns E by 4 fixed local frames, stored once for the entire
/// history.
const std::vector<ThermalElementFrames>& FluxLocalFrames() const noexcept;
private:
/// @brief Takes an exact schedule; Create then fills the validated inventory.
ThermalAnalysisState(ThermalTimeGrid grid, double nominal, double period);
/// @brief Checks frame metadata, mapping, payload shape and finite values.
Status ValidateFrame(const ThermalAcceptedFrame& frame) const;
ThermalTimeGrid grid_;
double nominal_;
double period_;
std::vector<EntityIndex> node_order_;
std::vector<SourceEntityId> node_source_ids_;
std::vector<EntityIndex> element_order_;
std::vector<SourceEntityId> element_source_ids_;
std::vector<std::array<std::uint8_t, 5>> constraint_mask_;
std::vector<ThermalReactionDof> reaction_dofs_;
std::vector<ThermalElementFrames> local_frames_;
std::vector<ThermalAcceptedFrame> frames_;
};
} // namespace fesa
#endif // FESA_ANALYSIS_THERMAL_ANALYSIS_STATE_H_
+87
View File
@@ -0,0 +1,87 @@
#ifndef FESA_ASSEMBLY_THERMAL_ASSEMBLER_H_
#define FESA_ASSEMBLY_THERMAL_ASSEMBLER_H_
#include <array>
#include <vector>
#include "fesa/analysis/analysis_model.h"
#include "fesa/assembly/parallel_for.h"
#include "fesa/elements/thermal_shell.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/math/sparse_matrix.h"
#include "fesa/results/thermal_result_records.h"
namespace fesa {
class ThermalAssembly;
/// @brief Builds full K,C,Q from checked DS4 kernels in deterministic source
/// order.
/// @note Domain must outlive the returned assembly; neither model nor dofs is
/// retained. Workers write index-owned buffers only. Failures are selected in
/// element order.
Result<ThermalAssembly> AssembleThermal(const AnalysisModel& model,
const DofManager& dofs,
const ParallelFor& parallel_for);
/// @brief Owns full-space operators and local kernels for a fixed thermal
/// Domain.
/// @note The original Domain must remain alive and unmoved for this object's
/// lifetime.
class ThermalAssembly final {
public:
/// @brief Returns full conductivity [power/temperature] in canonical CSR
/// order.
const SparseMatrix& Conductivity() const noexcept;
/// @brief Returns full consistent capacity [energy/temperature].
const SparseMatrix& Capacity() const noexcept;
/// @brief Returns signed full nodal input [power], including prescribed
/// targets.
const Vector& HeatInput() const noexcept;
/// @brief Bounds local interval error plus ordered summation roundoff
/// entrywise.
/// @note Excludes quadrature truncation and physical model error.
const SparseMatrix& ConductivityError() const noexcept;
/// @copydoc ConductivityError
const SparseMatrix& CapacityError() const noexcept;
/// @brief Bounds source-row input summation roundoff entrywise, in power
/// units.
const Vector& HeatInputError() const noexcept;
/// @brief Returns original Domain element IDs in local-kernel/flux row order.
const std::vector<EntityIndex>& ElementOrder() const noexcept;
/// @brief Returns checked kernels in stable active element order.
const std::vector<ThermalShell>& Kernels() const noexcept;
/// @brief Returns node-major twenty-entry full DOF scatters in kernel order.
const std::vector<std::array<std::size_t, 20>>& Scatters() const noexcept;
/// @brief Checks source Domain identity and the complete supplied equation
/// map.
Status ValidateFor(const AnalysisModel& model, const DofManager& dofs) const;
/// @brief Recovers ordered element-global fluxes from a finite full
/// temperature.
Result<std::vector<ThermalElementFlux>> RecoverFlux(
const Vector& temperature, const ParallelFor& parallel_for) const;
private:
friend Result<ThermalAssembly> AssembleThermal(const AnalysisModel&,
const DofManager&,
const ParallelFor&);
/// @brief Takes completed immutable operators after validation and reduction.
ThermalAssembly(const Domain& domain, SparseMatrix conductivity,
SparseMatrix capacity, Vector input,
SparseMatrix conductivity_error, SparseMatrix capacity_error,
Vector input_error, std::vector<EntityIndex> element_order,
std::vector<ThermalShell> kernels,
std::vector<std::array<std::size_t, 20>> scatters);
const Domain* domain_;
SparseMatrix conductivity_;
SparseMatrix capacity_;
Vector input_;
SparseMatrix conductivity_error_;
SparseMatrix capacity_error_;
Vector input_error_;
std::vector<EntityIndex> element_order_;
std::vector<ThermalShell> kernels_;
std::vector<std::array<std::size_t, 20>> scatters_;
};
} // namespace fesa
#endif // FESA_ASSEMBLY_THERMAL_ASSEMBLER_H_
@@ -0,0 +1,53 @@
#ifndef FESA_RESULTS_THERMAL_RESULT_RECORDS_H_
#define FESA_RESULTS_THERMAL_RESULT_RECORDS_H_
#include <array>
#include <cstdint>
#include <optional>
#include <vector>
#include "fesa/core/source_identity.h"
#include "fesa/math/vector.h"
#include "fesa/math/vector3.h"
namespace fesa {
/// @brief Identifies a prescribed temperature by Domain node and source DOF
/// 11/15.
struct ThermalReactionDof {
EntityIndex node;
int source_dof;
};
/// @brief Holds four CCW surface points times six ordered global flux traces.
/// @note Row=6*point+trace; traces are -1+, -.5, 0-, 0+, .5, 1-.
using ThermalElementFlux = std::array<Vector3, 24>;
/// @brief Holds R columns e1,e2,n at each of four CCW surface points.
using ThermalElementFrames = std::array<std::array<Vector3, 3>, 4>;
/// @brief Holds recovery present only at completed increments, including P=0.
struct ThermalCompletedPayload {
std::vector<ThermalReactionDof> reaction_dofs;
Vector reaction_heat_rate{0U}; // Power, positive into the model.
std::vector<EntityIndex> element_order;
std::vector<ThermalElementFlux> global_heat_flux; // Power/area.
};
/// @brief Carries an initial or completed thermal frame with explicit row
/// mapping.
/// @note Source identities are resolved through the state's immutable
/// inventory.
struct ThermalAcceptedFrame {
std::uint64_t frame_index = 0U;
std::uint64_t increment_index = 0U;
double time = 0;
double time_increment = 0;
bool is_initial = true;
std::vector<EntityIndex> node_order;
Vector temperature{
0U}; // Node-major, source DOFs 11..15; signed temperature.
std::optional<ThermalCompletedPayload> completed;
};
} // namespace fesa
#endif // FESA_RESULTS_THERMAL_RESULT_RECORDS_H_