kiran 1.0.0

Kiran — AI-native game engine for AGNOS
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
//! Advanced dynamics via bijli, dravya, ushma, and pavan
//!
//! Bridges the AGNOS dynamics stack with kiran's ECS:
//! - **bijli** — Electromagnetism (fields, charges, waves, circuits, FDTD)
//! - **dravya** — Material science (stress, strain, elasticity, fatigue, fracture)
//! - **ushma** — Thermodynamics (heat transfer, phase transitions, equations of state)
//! - **pavan** — Aerodynamics (atmosphere, airfoils, forces, wind, panel/VLM methods)
//!
//! Core types:
//! - [`EmField`] component for electromagnetic field sources
//! - [`MaterialBody`] component for stress/strain simulation
//! - [`ThermalBody`] component for heat transfer simulation
//! - [`AeroSurface`] component for aerodynamic force computation

use serde::{Deserialize, Serialize};

// ---------------------------------------------------------------------------
// bijli — electromagnetism
// ---------------------------------------------------------------------------

/// Point charges and Coulomb interactions.
pub use bijli::charge;
/// Electrical circuits (Ohm's law, impedance, RC/RL/RLC).
pub use bijli::circuit;
/// Electric and magnetic field calculations.
pub use bijli::field;
/// Dielectric and magnetic material properties.
pub use bijli::material as em_material;
/// Maxwell's equations solvers.
pub use bijli::maxwell;
/// Polarization (Jones vectors, Mueller matrices).
pub use bijli::polarization;
/// Scattering (Mie, Rayleigh).
pub use bijli::scattering;
/// EM wave propagation.
pub use bijli::wave as em_wave;

pub use bijli::BijliError;

// ---------------------------------------------------------------------------
// dravya — material science
// ---------------------------------------------------------------------------

/// Composite laminate theory and failure criteria.
pub use dravya::composite;
/// Constitutive models (bilinear, Ramberg-Osgood, Neo-Hookean, Johnson-Cook).
pub use dravya::constitutive;
/// Elastic moduli and Hooke's law.
pub use dravya::elastic;
/// Fatigue models (Basquin, Coffin-Manson, Miner's rule).
pub use dravya::fatigue;
/// Fracture mechanics (stress intensity, Paris law).
pub use dravya::fracture;
/// Material definitions and mechanical properties.
pub use dravya::material as mech_material;
/// Strain tensor (Voigt notation).
pub use dravya::strain;
/// Stress tensor (Voigt notation).
pub use dravya::stress;
/// Yield criteria (von Mises, Tresca, Drucker-Prager).
pub use dravya::yield_criteria;

pub use dravya::DravyaError;

// ---------------------------------------------------------------------------
// ushma — thermodynamics
// ---------------------------------------------------------------------------

/// Chemical thermodynamics (Hess's law, equilibrium).
pub use ushma::chem as thermo_chem;
/// Thermodynamic cycles (Otto, Diesel, Brayton, Rankine).
pub use ushma::cycle;
/// Entropy, free energy, thermodynamic potentials.
pub use ushma::entropy;
/// Thermal material properties (conductivity, specific heat, density).
pub use ushma::material as thermal_material;
/// Phase transitions (Clausius-Clapeyron).
pub use ushma::phase;
/// Statistical thermodynamics (partition functions).
pub use ushma::stat as thermo_stat;
/// Equations of state (ideal gas, van der Waals, Peng-Robinson).
pub use ushma::state as thermo_state;
/// Heat transfer (conduction, convection, radiation).
pub use ushma::transfer;

pub use ushma::UshmaError;

// ---------------------------------------------------------------------------
// pavan — aerodynamics
// ---------------------------------------------------------------------------

/// NACA airfoil generation.
pub use pavan::airfoil;
/// ISA standard atmosphere.
pub use pavan::atmosphere as aero_atmosphere;
/// Boundary layer analysis.
pub use pavan::boundary;
/// Lift/drag coefficient models.
pub use pavan::coefficients;
/// Aerodynamic forces (lift, drag, moment).
pub use pavan::forces as aero_forces;
/// 2D panel method (Hess-Smith).
pub use pavan::panel;
/// Flight stability analysis.
pub use pavan::stability;
/// Aerodynamic body definition.
pub use pavan::vehicle;
/// Vortex Lattice Method (3D finite wings).
pub use pavan::vlm;
/// Wind field modeling.
pub use pavan::wind;

pub use pavan::PavanError;

// ---------------------------------------------------------------------------
// EM field component
// ---------------------------------------------------------------------------

/// Electromagnetic field source attached to an entity.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmField {
    /// Electric field vector (V/m).
    pub electric: [f64; 3],
    /// Magnetic field vector (T).
    pub magnetic: [f64; 3],
    /// Whether this source is active.
    pub active: bool,
}

impl EmField {
    /// Create a new EM field source.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[cfg(feature = "dynamics")] {
    /// use kiran::dynamics::EmField;
    ///
    /// let field = EmField::new([1.0, 0.0, 0.0], [0.0, 0.0, 0.5]);
    /// assert!(field.active);
    /// assert_eq!(field.electric[0], 1.0);
    /// # }
    /// ```
    pub fn new(electric: [f64; 3], magnetic: [f64; 3]) -> Self {
        tracing::trace!(
            electric_x = electric[0],
            electric_y = electric[1],
            electric_z = electric[2],
            magnetic_x = magnetic[0],
            magnetic_y = magnetic[1],
            magnetic_z = magnetic[2],
            "created EM field"
        );
        Self {
            electric,
            magnetic,
            active: true,
        }
    }

    /// Create a purely electric field.
    pub fn electric_only(e: [f64; 3]) -> Self {
        Self::new(e, [0.0; 3])
    }

    /// Create a purely magnetic field.
    pub fn magnetic_only(b: [f64; 3]) -> Self {
        Self::new([0.0; 3], b)
    }
}

impl Default for EmField {
    fn default() -> Self {
        Self::new([0.0; 3], [0.0; 3])
    }
}

// ---------------------------------------------------------------------------
// Material body component
// ---------------------------------------------------------------------------

/// Material body for stress/strain simulation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaterialBody {
    /// Young's modulus (Pa).
    pub youngs_modulus: f64,
    /// Poisson's ratio.
    pub poisson_ratio: f64,
    /// Yield strength (Pa).
    pub yield_strength: f64,
    /// Density (kg/m³).
    pub density: f64,
    /// Accumulated fatigue damage (0.0 = pristine, 1.0 = failure).
    pub fatigue_damage: f64,
    /// Whether this body is active in simulation.
    pub active: bool,
}

impl MaterialBody {
    /// Create a material body from basic properties.
    pub fn new(youngs_modulus: f64, poisson_ratio: f64, yield_strength: f64, density: f64) -> Self {
        tracing::trace!(
            youngs_modulus,
            poisson_ratio,
            yield_strength,
            density,
            "created material body"
        );
        Self {
            youngs_modulus,
            poisson_ratio,
            yield_strength,
            density,
            fatigue_damage: 0.0,
            active: true,
        }
    }

    /// Create a steel material body.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[cfg(feature = "dynamics")] {
    /// use kiran::dynamics::MaterialBody;
    ///
    /// let steel = MaterialBody::steel();
    /// assert_eq!(steel.youngs_modulus, 200e9);
    /// assert!(!steel.is_failed());
    /// # }
    /// ```
    pub fn steel() -> Self {
        Self::new(200e9, 0.3, 250e6, 7850.0)
    }

    /// Create an aluminum material body.
    pub fn aluminum() -> Self {
        Self::new(69e9, 0.33, 276e6, 2700.0)
    }

    /// Whether this material has exceeded its yield strength.
    #[must_use]
    #[inline]
    pub fn is_yielded(&self, stress: f64) -> bool {
        stress >= self.yield_strength
    }

    /// Whether fatigue failure has occurred.
    #[must_use]
    #[inline]
    pub fn is_failed(&self) -> bool {
        self.fatigue_damage >= 1.0
    }
}

// ---------------------------------------------------------------------------
// Thermal body component
// ---------------------------------------------------------------------------

/// Thermal body for heat transfer simulation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThermalBody {
    /// Current temperature (K).
    pub temperature: f64,
    /// Thermal conductivity (W/m·K).
    pub conductivity: f64,
    /// Specific heat capacity (J/kg·K).
    pub specific_heat: f64,
    /// Mass (kg).
    pub mass: f64,
    /// Current phase of matter.
    pub phase: ThermalPhase,
    /// Whether this body is active in simulation.
    pub active: bool,
}

/// Phase of matter for a thermal body.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[non_exhaustive]
pub enum ThermalPhase {
    /// Solid state.
    Solid,
    /// Liquid state.
    Liquid,
    /// Gaseous state.
    Gas,
}

impl ThermalBody {
    /// Create a new thermal body.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[cfg(feature = "dynamics")] {
    /// use kiran::dynamics::ThermalBody;
    ///
    /// let body = ThermalBody::new(300.0, 50.0, 500.0, 1.0);
    /// assert_eq!(body.temperature, 300.0);
    /// assert!(body.active);
    /// # }
    /// ```
    pub fn new(temperature: f64, conductivity: f64, specific_heat: f64, mass: f64) -> Self {
        tracing::trace!(
            temperature,
            conductivity,
            specific_heat,
            mass,
            "created thermal body"
        );
        Self {
            temperature,
            conductivity,
            specific_heat,
            mass,
            phase: ThermalPhase::Solid,
            active: true,
        }
    }

    /// Set the phase.
    pub fn with_phase(mut self, phase: ThermalPhase) -> Self {
        self.phase = phase;
        self
    }

    /// Apply heat energy (J) and update temperature.
    pub fn apply_heat(&mut self, energy_joules: f64) {
        if self.mass > 0.0 && self.specific_heat > 0.0 {
            self.temperature += energy_joules / (self.mass * self.specific_heat);
        }
        tracing::debug!(
            energy = energy_joules,
            new_temp = self.temperature,
            "applied heat"
        );
    }
}

// ---------------------------------------------------------------------------
// Aero surface component
// ---------------------------------------------------------------------------

/// Aerodynamic surface for force computation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AeroSurface {
    /// Reference area (m²).
    pub reference_area: f64,
    /// Parasitic drag coefficient.
    pub cd0: f64,
    /// Wing aspect ratio.
    pub aspect_ratio: f64,
    /// Oswald efficiency factor.
    pub oswald_efficiency: f64,
    /// Current angle of attack (radians).
    pub angle_of_attack: f64,
    /// Whether this surface is active.
    pub active: bool,
}

impl AeroSurface {
    /// Create a new aerodynamic surface.
    ///
    /// # Examples
    ///
    /// ```
    /// # #[cfg(feature = "dynamics")] {
    /// use kiran::dynamics::AeroSurface;
    ///
    /// let wing = AeroSurface::new(16.0, 0.02, 8.0);
    /// assert_eq!(wing.reference_area, 16.0);
    /// assert!(wing.active);
    /// # }
    /// ```
    pub fn new(reference_area: f64, cd0: f64, aspect_ratio: f64) -> Self {
        tracing::trace!(reference_area, cd0, aspect_ratio, "created aero surface");
        Self {
            reference_area,
            cd0,
            aspect_ratio,
            oswald_efficiency: 0.85,
            angle_of_attack: 0.0,
            active: true,
        }
    }

    /// Set the Oswald efficiency factor.
    pub fn with_oswald(mut self, e: f64) -> Self {
        self.oswald_efficiency = e;
        self
    }

    /// Set the angle of attack in radians.
    pub fn with_aoa(mut self, aoa: f64) -> Self {
        self.angle_of_attack = aoa;
        self
    }
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn em_field_default() {
        let f = EmField::default();
        assert_eq!(f.electric, [0.0; 3]);
        assert_eq!(f.magnetic, [0.0; 3]);
        assert!(f.active);
    }

    #[test]
    fn em_field_electric_only() {
        let f = EmField::electric_only([1.0, 0.0, 0.0]);
        assert_eq!(f.electric[0], 1.0);
        assert_eq!(f.magnetic, [0.0; 3]);
    }

    #[test]
    fn material_body_steel() {
        let m = MaterialBody::steel();
        assert_eq!(m.youngs_modulus, 200e9);
        assert!(!m.is_failed());
    }

    #[test]
    fn material_body_yield() {
        let m = MaterialBody::steel();
        assert!(!m.is_yielded(100e6));
        assert!(m.is_yielded(300e6));
    }

    #[test]
    fn thermal_body_heat() {
        let mut t = ThermalBody::new(300.0, 50.0, 500.0, 1.0);
        t.apply_heat(500.0); // 500J into 1kg @ 500 J/kg·K = +1K
        assert!((t.temperature - 301.0).abs() < 1e-10);
    }

    #[test]
    fn thermal_body_phase() {
        let t = ThermalBody::new(373.0, 0.6, 4186.0, 1.0).with_phase(ThermalPhase::Liquid);
        assert_eq!(t.phase, ThermalPhase::Liquid);
    }

    #[test]
    fn aero_surface_builder() {
        let s = AeroSurface::new(16.0, 0.02, 8.0)
            .with_oswald(0.9)
            .with_aoa(0.1);
        assert_eq!(s.reference_area, 16.0);
        assert_eq!(s.oswald_efficiency, 0.9);
        assert_eq!(s.angle_of_attack, 0.1);
    }

    #[test]
    fn em_field_as_component() {
        let mut world = crate::World::new();
        let e = world.spawn();
        world
            .insert_component(e, EmField::electric_only([5.0, 0.0, 0.0]))
            .unwrap();
        assert!(world.has_component::<EmField>(e));
    }

    #[test]
    fn material_body_as_component() {
        let mut world = crate::World::new();
        let e = world.spawn();
        world.insert_component(e, MaterialBody::steel()).unwrap();
        let m = world.get_component::<MaterialBody>(e).unwrap();
        assert_eq!(m.density, 7850.0);
    }

    #[test]
    fn thermal_body_as_component() {
        let mut world = crate::World::new();
        let e = world.spawn();
        world
            .insert_component(e, ThermalBody::new(293.0, 50.0, 500.0, 10.0))
            .unwrap();
        assert!(world.has_component::<ThermalBody>(e));
    }
}