Skip to content

Commit f6f98f1

Browse files
authored
Merge pull request erematorg#139 from M1thieu/dev
Energy Perception & Physics Stability
2 parents ac385c3 + 2cda965 commit f6f98f1

36 files changed

Lines changed: 1992 additions & 488 deletions

.gitignore

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,13 @@ Thumbs.db
2828
*.temp
2929
*.log
3030
/tmp
31+
.claude/
32+
CLAUDE.md
3133

3234
# Environment files
3335
.env
3436
.env.local
3537
.env.*.local
3638

3739
# Application save files
38-
save.json
39-
40-
# MCP and Serena files
41-
.mcp.json
42-
.serena/
43-
.claude/
44-
CLAUDE.md
40+
save.json

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ members = [
1212
"crates/forces",
1313
"crates/information",
1414
"crates/matter",
15+
"crates/utils",
1516
]
1617

1718
# Enable a small amount of optimization in the dev profile.
@@ -29,6 +30,7 @@ energy = { path = "crates/energy" }
2930
forces = { path = "crates/forces" }
3031
information = { path = "crates/information" }
3132
matter = { path = "crates/matter" }
33+
utils = { path = "crates/utils" }
3234

3335
bevy = { version = "0.17", features = ["dynamic_linking"] }
3436

crates/energy/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ description = "Core systems governing how energy manifests, transforms and flows
66

77
[dependencies]
88
bevy = "0.17"
9+
utils = { path = "../utils" }

crates/energy/README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
# Energy
22

3-
Energy conservation, thermodynamics, electromagnetism, and wave mechanics for LP physics simulations.
3+
Energy conservation, thermodynamics, electromagnetism, and wave mechanics for LP physics simulations.
4+
5+
## Scope and units
6+
- Tracks energy in joules via an accounting ledger; conservation is enforced where modeled.
7+
- Thermodynamics, EM, and waves modules are present but remain partial scaffolds.
8+
- Uses consistent sim units (SI-style); couple to time/space steps used by the broader sim.
9+
10+
## Not yet implemented
11+
- **Wave/EM energy accounting**: Wave damping exists but not coupled to energy ledger; EM fields exist but no work/energy tracking.
12+
- **EOS, convection, latent heat, phase transitions**: Deferred to matter/MPM coupling (see tmp/GD/Systems_Overview.md).

crates/energy/src/conservation.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,33 @@ pub fn conversion_efficiency(energy_input: f32, energy_output: f32) -> f32 {
231231
}
232232
}
233233

234+
/// Optional resource to monitor energy drift over time (diagnostic only, not enforced)
235+
#[derive(Resource, Debug)]
236+
pub struct EnergyDriftMonitor {
237+
pub initial_energy: f32,
238+
pub tolerance: f32,
239+
}
240+
241+
impl EnergyDriftMonitor {
242+
pub fn new(initial_energy: f32, tolerance: f32) -> Self {
243+
Self {
244+
initial_energy,
245+
tolerance,
246+
}
247+
}
248+
249+
/// Check if current energy has drifted beyond tolerance
250+
/// Returns Some(drift) if drift exceeds tolerance, None otherwise
251+
pub fn check_drift(&self, current_energy: f32) -> Option<f32> {
252+
let drift = (current_energy - self.initial_energy).abs();
253+
if drift > self.tolerance {
254+
Some(drift)
255+
} else {
256+
None
257+
}
258+
}
259+
}
260+
234261
/// Plugin to manage energy conservation systems
235262
pub struct EnergyConservationPlugin;
236263

@@ -249,3 +276,92 @@ impl Plugin for EnergyConservationPlugin {
249276
.add_message::<EnergyTransferEvent>();
250277
}
251278
}
279+
280+
#[cfg(test)]
281+
mod tests {
282+
use super::*;
283+
284+
#[test]
285+
fn test_ledger_arithmetic() {
286+
// Test that net_energy_change = total_input - total_output
287+
let mut ledger = EnergyAccountingLedger::default();
288+
289+
ledger.record_transaction(EnergyTransaction {
290+
transaction_type: TransactionType::Input,
291+
amount: 100.0,
292+
source: None,
293+
destination: None,
294+
timestamp: 0.0,
295+
transfer_rate: 0.0,
296+
duration: 0.0,
297+
});
298+
299+
ledger.record_transaction(EnergyTransaction {
300+
transaction_type: TransactionType::Output,
301+
amount: 30.0,
302+
source: None,
303+
destination: None,
304+
timestamp: 0.0,
305+
transfer_rate: 0.0,
306+
duration: 0.0,
307+
});
308+
309+
ledger.record_transaction(EnergyTransaction {
310+
transaction_type: TransactionType::Input,
311+
amount: 50.0,
312+
source: None,
313+
destination: None,
314+
timestamp: 0.0,
315+
transfer_rate: 0.0,
316+
duration: 0.0,
317+
});
318+
319+
assert_eq!(ledger.total_input, 150.0);
320+
assert_eq!(ledger.total_output, 30.0);
321+
assert_eq!(ledger.net_energy_change(), 120.0);
322+
}
323+
324+
#[test]
325+
fn test_current_flux_sums_active_rates() {
326+
// Test that current_flux sums transfer rates correctly
327+
let mut ledger = EnergyAccountingLedger::default();
328+
329+
let current_time = 10.0;
330+
331+
// Add active transfer (within time window)
332+
ledger.record_transaction(EnergyTransaction {
333+
transaction_type: TransactionType::Input,
334+
amount: 50.0,
335+
source: None,
336+
destination: None,
337+
timestamp: 9.5,
338+
transfer_rate: 10.0, // W
339+
duration: 1.0,
340+
});
341+
342+
// Add another active transfer
343+
ledger.record_transaction(EnergyTransaction {
344+
transaction_type: TransactionType::Input,
345+
amount: 30.0,
346+
source: None,
347+
destination: None,
348+
timestamp: 9.8,
349+
transfer_rate: 5.0, // W
350+
duration: 0.5,
351+
});
352+
353+
// Add old transfer (outside time window)
354+
ledger.record_transaction(EnergyTransaction {
355+
transaction_type: TransactionType::Input,
356+
amount: 100.0,
357+
source: None,
358+
destination: None,
359+
timestamp: 5.0,
360+
transfer_rate: 20.0, // W
361+
duration: 2.0,
362+
});
363+
364+
let flux = ledger.current_flux(current_time, 1.0);
365+
assert_eq!(flux, 15.0, "Expected sum of active rates: 10.0 + 5.0");
366+
}
367+
}

crates/energy/src/electromagnetism/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ pub mod interactions;
33

44
use bevy::prelude::*;
55

6+
// NOTE: Charge is NOT conserved; EM is quasi-static (no charge continuity equation).
7+
// Ref: .claude/skills/lp-physics-chem-invariants/references/em.md
68
pub struct ElectromagnetismPlugin;
79

810
impl Plugin for ElectromagnetismPlugin {

crates/energy/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ pub mod prelude {
114114

115115
pub use crate::conservation::{
116116
EnergyAccountingLedger, EnergyConservationPlugin, EnergyConservationTracker,
117-
EnergyQuantity, EnergyTransaction, EnergyTransferEvent, EnergyType, TransactionType,
118-
conversion_efficiency, verify_conservation,
117+
EnergyDriftMonitor, EnergyQuantity, EnergyTransaction, EnergyTransferEvent, EnergyType,
118+
TransactionType, conversion_efficiency, verify_conservation,
119119
};
120120

121121
pub use crate::electromagnetism::prelude::*;

crates/energy/src/thermodynamics/entropy.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub struct Entropy {
77
/// Entropy in J/K
88
pub value: f32,
99
}
10+
// NOTE: No systems currently update Entropy in the world; helper functions return ΔS but callers must apply them.
1011

1112
impl Entropy {
1213
pub fn new(value: f32) -> Self {

crates/energy/src/thermodynamics/equilibrium.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,17 @@ pub struct ThermalProperties {
2828
}
2929

3030
/// Check if two systems are in thermal equilibrium
31+
/// Zeroth Law: Equilibrium means equal temperature (within tolerance)
3132
pub fn is_in_equilibrium(
3233
temp_a: f32,
3334
temp_b: f32,
34-
props_a: &ThermalProperties,
35-
props_b: &ThermalProperties,
35+
_props_a: &ThermalProperties,
36+
_props_b: &ThermalProperties,
3637
tolerance: f32,
3738
) -> bool {
38-
// Weighted equilibrium considers both temperature and thermal properties
39-
let weighted_diff =
40-
(temp_a - temp_b).abs() / (1.0 + (props_a.thermal_mass * props_b.thermal_mass).sqrt());
41-
weighted_diff <= tolerance
39+
// Zeroth Law: Thermal equilibrium is defined purely by temperature equality
40+
// Thermal mass affects TIME to reach equilibrium, not the definition of equilibrium itself
41+
(temp_a - temp_b).abs() <= tolerance
4242
}
4343

4444
pub fn equilibrium_time_estimate(

crates/energy/src/thermodynamics/mod.rs

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,11 @@ pub struct ThermodynamicsPlugin;
1616

1717
impl Plugin for ThermodynamicsPlugin {
1818
fn build(&self, app: &mut App) {
19-
app.register_type::<thermal::Temperature>()
20-
.register_type::<thermal::ThermalConductivity>()
21-
.register_type::<thermal::ThermalDiffusivity>()
19+
app.add_plugins(thermal::ThermalSystemPlugin)
2220
.register_type::<entropy::Entropy>()
2321
.register_type::<entropy::Reversibility>()
2422
.register_type::<equilibrium::ThermalEquilibrium>()
25-
.register_type::<equilibrium::PhaseState>()
26-
.add_message::<thermal::ThermalTransferEvent>()
27-
.configure_sets(
28-
Update,
29-
(
30-
ThermodynamicsSet::ThermalTransfer,
31-
ThermodynamicsSet::Equilibrium,
32-
)
33-
.chain(),
34-
)
35-
.add_systems(
36-
Update,
37-
thermal::calculate_thermal_transfer.in_set(ThermodynamicsSet::ThermalTransfer),
38-
);
23+
.register_type::<equilibrium::PhaseState>();
3924
}
4025
}
4126

@@ -50,6 +35,7 @@ pub mod prelude {
5035
validate_equilibrium_group_consistency,
5136
};
5237
pub use super::thermal::{
53-
Temperature, ThermalConductivity, ThermalDiffusivity, thermal_utils::heat_conduction,
38+
Temperature, ThermalConductivity, ThermalDiffusivity, HeatCapacity,
39+
thermal_utils::heat_conduction,
5440
};
5541
}

0 commit comments

Comments
 (0)