BT410 Phase 5.3.14: AggregateHeatSink -- the bank + the ambient radiator (the heat EXIT)
The central heat bank is now its real class: the binary's 0x0BBE AggregateHeatSink (the value our VDATA enum named HeatSinkClassID -- there is no streamed plain HeatSink; the segment walk now builds the bank). - Ctor (@4ae8d0): heatSinkCount from res +0xFC (bhk1 = 6, matching its six condensers); thermalConductance x 0.1 x count (231000 -> 138600); ambient setpoint 300 (the mission [mission] temperature overwrite joins the Mech-PlayerLink wave). - RadiatorSimulation (@4ae73c) replaces the base heat step on the bank -- THE system's only heat exit: relax toward the ambient target with rate k = conductance x (1-damage) x (coolant/capacity) x flowScale / mass; tail tops the bank's coolant from the attached store via the DrawCoolant virtual (base 0 until the reservoir-attach wave). VERIFIED signed-correct: the bank warms 77 -> 300 from the cold start, then flips to actively radiating (-48K..-167K/step) once fired heat pushes it past ambient. Until now heat only ever POOLED in the central sink; the mech now genuinely sheds it. - Reservoir master path: capacity = 0.05 x bankCount x streamed = 6 (the authentic tank), refilled. Zero Fail; the expert economy regression stays green (202 heat events under forced spam). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -946,3 +946,138 @@ void
|
||||
DEBUG_STREAM << flush;
|
||||
}
|
||||
}
|
||||
|
||||
//###########################################################################
|
||||
//######################### AggregateHeatSink ###########################
|
||||
//###########################################################################
|
||||
|
||||
Derivation
|
||||
AggregateHeatSink::ClassDerivations(
|
||||
HeatSink::ClassDerivations,
|
||||
"HeatSinkBank"
|
||||
);
|
||||
|
||||
AggregateHeatSink::SharedData
|
||||
AggregateHeatSink::DefaultData(
|
||||
AggregateHeatSink::ClassDerivations,
|
||||
HeatSink::MessageHandlers,
|
||||
Subsystem::AttributeIndex,
|
||||
Subsystem::StateCount
|
||||
);
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// The heat-sink bank (binary ctor @4ae8d0): the aggregate count scales the
|
||||
// conductance (0.1 x count, byte-verified), the ambient setpoint defaults
|
||||
// 300 K (the mission's [mission] temperature overwrites it in the PlayerLink
|
||||
// pass -- that override joins the Mech-PlayerLink wave), and a master runs
|
||||
// the RADIATOR Performance instead of the base heat step.
|
||||
//#############################################################################
|
||||
//
|
||||
AggregateHeatSink::AggregateHeatSink(
|
||||
Mech *owner,
|
||||
int subsystem_ID,
|
||||
SubsystemResource *subsystem_resource,
|
||||
SharedData &shared_data
|
||||
):
|
||||
HeatSink(owner, subsystem_ID, subsystem_resource, shared_data)
|
||||
{
|
||||
Check(owner);
|
||||
Check_Pointer(subsystem_resource);
|
||||
|
||||
heatSinkCount = subsystem_resource->heatSinkCount;
|
||||
ambientTemperature = 300.0f;
|
||||
|
||||
thermalConductance = 0.1f * (Scalar)heatSinkCount * thermalConductance;
|
||||
|
||||
if (getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[bank] '" << GetName()
|
||||
<< "' heatSinkCount=" << heatSinkCount
|
||||
<< " conductance=" << thermalConductance << endl << flush;
|
||||
}
|
||||
|
||||
if (owner->GetInstance() != Entity::ReplicantInstance)
|
||||
{
|
||||
SetPerformance(&AggregateHeatSink::RadiatorSimulation);
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
AggregateHeatSink::~AggregateHeatSink()
|
||||
{
|
||||
}
|
||||
|
||||
Logical
|
||||
AggregateHeatSink::TestClass(Mech &)
|
||||
{
|
||||
return True;
|
||||
}
|
||||
|
||||
Logical
|
||||
AggregateHeatSink::TestInstance() const
|
||||
{
|
||||
return IsDerivedFrom(ClassDerivations);
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// RadiatorSimulation -- the bank's per-frame step (binary @4ae73c), replacing
|
||||
// the base HeatSinkSimulation: under the heat-model gate, absorb + recompute
|
||||
// the load, then the AMBIENT RADIATOR -- relax the bank toward the setpoint
|
||||
// target (300 - (300 - ambient) x 3) with rate k = conductance x (1 - own
|
||||
// damage) x (coolant/capacity) x flowScale / mass. This is the system's ONLY
|
||||
// heat exit. Tail: top the bank's coolant up from the attached store via the
|
||||
// DrawCoolant virtual (the reservoir-attach routing joins that wave; the base
|
||||
// supplies 0 until then, a harmless no-op).
|
||||
//#############################################################################
|
||||
//
|
||||
void
|
||||
AggregateHeatSink::RadiatorSimulation(Scalar time_slice)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (HeatModelActive())
|
||||
{
|
||||
heatEnergy += pendingHeat;
|
||||
currentTemperature = heatEnergy / thermalMass;
|
||||
UpdateHeatLoad();
|
||||
pendingHeat = 0.0f;
|
||||
|
||||
Scalar target = 300.0f - (300.0f - ambientTemperature) * 3.0f;
|
||||
Scalar decay = 1.0f - (Scalar)exp(
|
||||
(double)(-(time_slice * thermalConductance
|
||||
* (1.0f - GetSubsystemDamageLevel())
|
||||
* (coolantLevel / thermalCapacity) * coolantFlowScale)
|
||||
/ thermalMass)
|
||||
);
|
||||
Scalar shed = -((currentTemperature * massScale - target)
|
||||
* thermalMass * decay);
|
||||
pendingHeat += shed;
|
||||
|
||||
if (getenv("BT_HEAT_LOG"))
|
||||
{
|
||||
static Scalar bankAccum = 0.0f;
|
||||
bankAccum += time_slice;
|
||||
if (bankAccum >= 5.0f)
|
||||
{
|
||||
bankAccum = 0.0f;
|
||||
DEBUG_STREAM << "[heat-t] " << GetName() << " (bank)"
|
||||
<< " T=" << currentTemperature
|
||||
<< " shed=" << shed
|
||||
<< " cool=" << coolantLevel << "/" << thermalCapacity
|
||||
<< " load=" << heatLoad << endl << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Coolant top-up from the attached store.
|
||||
//
|
||||
Scalar deficit = thermalCapacity - coolantLevel;
|
||||
if (deficit > 1.0e-4f)
|
||||
{
|
||||
coolantLevel += DrawCoolant(deficit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,4 +423,71 @@
|
||||
Scalar refrigerationFactor;
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//##################### AggregateHeatSink Resource ######################
|
||||
//###########################################################################
|
||||
|
||||
struct AggregateHeatSink__SubsystemResource:
|
||||
public HeatSink__SubsystemResource
|
||||
{
|
||||
int heatSinkCount;
|
||||
};
|
||||
|
||||
//###########################################################################
|
||||
//######################### AggregateHeatSink ###########################
|
||||
//###########################################################################
|
||||
//
|
||||
// The mech's heat-sink BANK (binary classID 0x0BBE -- the value our VDATA
|
||||
// enum names HeatSinkClassID; there is no streamed plain-HeatSink class).
|
||||
// Holds the aggregate heat-sink count and the ambient-temperature setpoint,
|
||||
// and runs the AMBIENT RADIATOR -- the only place heat ever LEAVES the mech.
|
||||
//
|
||||
class AggregateHeatSink:
|
||||
public HeatSink
|
||||
{
|
||||
public:
|
||||
static Derivation ClassDerivations;
|
||||
static SharedData DefaultData;
|
||||
|
||||
static Logical
|
||||
TestClass(Mech &);
|
||||
Logical
|
||||
TestInstance() const;
|
||||
|
||||
public:
|
||||
typedef AggregateHeatSink__SubsystemResource SubsystemResource;
|
||||
|
||||
AggregateHeatSink(
|
||||
Mech *owner,
|
||||
int subsystem_ID,
|
||||
SubsystemResource *subsystem_resource,
|
||||
SharedData &shared_data = DefaultData
|
||||
);
|
||||
~AggregateHeatSink();
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// The radiator (binary @4ae73c) -- replaces the base HeatSinkSimulation on
|
||||
// the bank: absorb, then relax toward the ambient setpoint (the heat EXIT),
|
||||
// then top the bank's coolant up from the attached store.
|
||||
//
|
||||
public:
|
||||
typedef void
|
||||
(AggregateHeatSink::*Performance)(Scalar time_slice);
|
||||
void
|
||||
SetPerformance(Performance performance)
|
||||
{
|
||||
Check(this);
|
||||
activePerformance = (Simulation::Performance)performance;
|
||||
}
|
||||
void
|
||||
RadiatorSimulation(Scalar time_slice);
|
||||
|
||||
int
|
||||
GetHeatSinkCount() const { Check(this); return heatSinkCount; }
|
||||
|
||||
protected:
|
||||
int heatSinkCount;
|
||||
Scalar ambientTemperature;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -200,3 +200,26 @@ VERIFIED: spawn shares 6 x 0.166667; one MoveValve press -> Condenser1
|
||||
valve=5 flow=0.5, the others 0.1 (valve/sum exactly); the flush arms via the
|
||||
real button message; NOVICE: both presses swallowed by the lockout (valve
|
||||
lines stay spawn-only, zero flush). Zero Fail throughout.
|
||||
|
||||
## The heat-sink BANK (Phase 5.3.14, 2026-07-24)
|
||||
|
||||
**AggregateHeatSink reconstructed** -- the binary's 0x0BBE class (the value our
|
||||
VDATA enum names HeatSinkClassID; there is NO streamed plain-HeatSink -- the
|
||||
walk now builds the bank). Ctor (@4ae8d0): heatSinkCount from res +0xFC
|
||||
(bhk1 = 6, matching its six condensers), thermalConductance x 0.1 x count
|
||||
(231000 -> 138600), ambient setpoint 300 (the mission-temperature overwrite
|
||||
joins the Mech-PlayerLink wave).
|
||||
|
||||
**RadiatorSimulation** (@4ae73c) replaces the base heat step on the bank --
|
||||
THE system's only heat exit: relax toward the ambient target with rate
|
||||
k = conductance x (1 - damage) x (coolant/capacity) x flowScale / mass, then
|
||||
top the bank's coolant from the attached store via the DrawCoolant virtual
|
||||
(base 0 until the reservoir attach wave). VERIFIED signed-correct: the bank
|
||||
warms 77 -> 300 from the cold start (shed positive, absorbing toward
|
||||
ambient), then flips negative (-48K.. -167K/step) as fired heat pushes it
|
||||
past ambient -- actively radiating the mech's heat away.
|
||||
|
||||
**Reservoir master rescale**: capacity = 0.05 x bankCount x streamed
|
||||
(0.05 x 6 x 20 = 6 -- the authentic tank), coolant refilled to it.
|
||||
|
||||
Zero Fail; the full expert economy regression stays green.
|
||||
|
||||
@@ -239,7 +239,12 @@ Mech::Mech(
|
||||
made = new Condenser(this, id, (Condenser::SubsystemResource *)seg);
|
||||
break;
|
||||
case HeatSinkClassID:
|
||||
made = new HeatSink(this, id, (HeatSink::SubsystemResource *)seg);
|
||||
//
|
||||
// The 0x0BBE stream class is the mech's heat-sink BANK
|
||||
// (AggregateHeatSink) -- there is no streamed plain-HeatSink; our
|
||||
// VDATA enum name simply predates that discovery.
|
||||
//
|
||||
made = new AggregateHeatSink(this, id, (AggregateHeatSink::SubsystemResource *)seg);
|
||||
break;
|
||||
case HeatWatcherClassID:
|
||||
made = new HeatWatcher(this, id, (HeatWatcher::SubsystemResource *)seg);
|
||||
|
||||
@@ -101,6 +101,28 @@ Reservoir::Reservoir(
|
||||
if (owner->GetInstance() != Entity::ReplicantInstance)
|
||||
{
|
||||
SetPerformance(&Reservoir::CoolantSimulation);
|
||||
|
||||
//
|
||||
// The authentic master path scales the tank by the bank's aggregate
|
||||
// heat-sink count: capacity = 0.05 x count x streamed (binary
|
||||
// @4af408, CoolantCapacityScale byte-verified 0.05). The linked sink
|
||||
// (from the resource) IS the bank. (The bank-side Attach that routes
|
||||
// its DrawCoolant top-ups here joins the attach wave.)
|
||||
//
|
||||
HeatSink *link = (HeatSink *)linkedSinks.Resolve();
|
||||
if (link != NULL
|
||||
&& link->IsDerivedFrom(AggregateHeatSink::ClassDerivations))
|
||||
{
|
||||
Scalar masterScale =
|
||||
(Scalar)((AggregateHeatSink *)link)->GetHeatSinkCount();
|
||||
thermalCapacity = 0.05f * masterScale * thermalCapacity;
|
||||
coolantLevel = thermalCapacity;
|
||||
if (getenv("BT_POWER_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[resv] capacity rescaled by bank count -> "
|
||||
<< thermalCapacity << endl << flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
|
||||
Reference in New Issue
Block a user