feat(thermal): assemble heat operators and preserve typed frame history
This commit is contained in:
@@ -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_
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user