lfm-physics 0.2.2__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. lfm_physics-0.4.0/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  2. lfm_physics-0.4.0/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  3. lfm_physics-0.4.0/.github/PULL_REQUEST_TEMPLATE.md +19 -0
  4. lfm_physics-0.4.0/.pre-commit-config.yaml +20 -0
  5. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/CHANGELOG.md +41 -0
  6. lfm_physics-0.4.0/CODE_OF_CONDUCT.md +31 -0
  7. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/PKG-INFO +51 -1
  8. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/README.md +50 -0
  9. lfm_physics-0.4.0/SECURITY.md +21 -0
  10. lfm_physics-0.4.0/docs/Makefile +23 -0
  11. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/conf.py +1 -1
  12. lfm_physics-0.4.0/docs/make.bat +26 -0
  13. lfm_physics-0.4.0/docs/primer.md +117 -0
  14. lfm_physics-0.4.0/docs/troubleshooting.md +159 -0
  15. lfm_physics-0.4.0/examples/15_visualization.py +93 -0
  16. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/__init__.py +42 -1
  17. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/analysis/__init__.py +40 -0
  18. lfm_physics-0.4.0/lfm/analysis/angular_momentum.py +146 -0
  19. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/analysis/color.py +2 -1
  20. lfm_physics-0.4.0/lfm/analysis/metric.py +148 -0
  21. lfm_physics-0.4.0/lfm/analysis/phase.py +138 -0
  22. lfm_physics-0.4.0/lfm/analysis/spectrum.py +57 -0
  23. lfm_physics-0.4.0/lfm/analysis/tracker.py +80 -0
  24. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/backends/kernel_source.py +13 -8
  25. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/fields/__init__.py +2 -0
  26. lfm_physics-0.4.0/lfm/fields/boosted.py +107 -0
  27. lfm_physics-0.4.0/lfm/sweep.py +155 -0
  28. lfm_physics-0.4.0/lfm/viz/__init__.py +42 -0
  29. lfm_physics-0.4.0/lfm/viz/_util.py +14 -0
  30. lfm_physics-0.4.0/lfm/viz/evolution.py +118 -0
  31. lfm_physics-0.4.0/lfm/viz/fields.py +78 -0
  32. lfm_physics-0.4.0/lfm/viz/radial.py +87 -0
  33. lfm_physics-0.4.0/lfm/viz/slices.py +150 -0
  34. lfm_physics-0.4.0/lfm/viz/spectrum.py +71 -0
  35. lfm_physics-0.4.0/lfm/viz/sweep.py +60 -0
  36. lfm_physics-0.4.0/lfm/viz/tracker.py +75 -0
  37. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/pyproject.toml +16 -1
  38. lfm_physics-0.4.0/tests/test_coverage_boost.py +304 -0
  39. lfm_physics-0.4.0/tests/test_edge_cases.py +154 -0
  40. lfm_physics-0.4.0/tests/test_integration.py +208 -0
  41. lfm_physics-0.4.0/tests/test_physics_modules.py +275 -0
  42. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/.github/workflows/publish.yml +0 -0
  43. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/.github/workflows/test.yml +0 -0
  44. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/.gitignore +0 -0
  45. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/.readthedocs.yaml +0 -0
  46. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/CONTRIBUTING.md +0 -0
  47. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/LICENSE +0 -0
  48. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/benchmarks/README.md +0 -0
  49. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/benchmarks/bench_evolver.py +0 -0
  50. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/benchmarks/bench_fields.py +0 -0
  51. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/api/analysis.rst +0 -0
  52. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/api/config.rst +0 -0
  53. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/api/constants.rst +0 -0
  54. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/api/core.rst +0 -0
  55. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/api/fields.rst +0 -0
  56. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/api/io.rst +0 -0
  57. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/api/simulation.rst +0 -0
  58. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/api/units.rst +0 -0
  59. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/changelog.rst +0 -0
  60. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/contributing.rst +0 -0
  61. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/examples.md +0 -0
  62. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/index.rst +0 -0
  63. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/installation.md +0 -0
  64. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/quickstart.md +0 -0
  65. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/docs/requirements.txt +0 -0
  66. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/01_empty_space.py +0 -0
  67. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/02_first_particle.py +0 -0
  68. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/03_measuring_gravity.py +0 -0
  69. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/04_two_bodies.py +0 -0
  70. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/05_electric_charge.py +0 -0
  71. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/06_dark_matter.py +0 -0
  72. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/07_matter_creation.py +0 -0
  73. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/08_universe.py +0 -0
  74. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/09_hydrogen_atom.py +0 -0
  75. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/10_hydrogen_molecule.py +0 -0
  76. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/11_oxygen.py +0 -0
  77. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/12_fluid_dynamics.py +0 -0
  78. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/13_weak_force.py +0 -0
  79. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/examples/14_strong_force.py +0 -0
  80. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/analysis/energy.py +0 -0
  81. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/analysis/metrics.py +0 -0
  82. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/analysis/observables.py +0 -0
  83. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/analysis/structure.py +0 -0
  84. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/config.py +0 -0
  85. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/constants.py +0 -0
  86. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/__init__.py +0 -0
  87. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/backends/__init__.py +0 -0
  88. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/backends/cupy_backend.py +0 -0
  89. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/backends/numpy_backend.py +0 -0
  90. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/backends/protocol.py +0 -0
  91. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/evolver.py +0 -0
  92. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/integrator.py +0 -0
  93. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/core/stencils.py +0 -0
  94. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/fields/arrangements.py +0 -0
  95. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/fields/equilibrium.py +0 -0
  96. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/fields/random.py +0 -0
  97. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/fields/soliton.py +0 -0
  98. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/io/__init__.py +0 -0
  99. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/py.typed +0 -0
  100. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/simulation.py +0 -0
  101. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/lfm/units.py +0 -0
  102. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/paper_experiments/why_is_c_what_it_is.py +0 -0
  103. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/__init__.py +0 -0
  104. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/conftest.py +0 -0
  105. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_analysis.py +0 -0
  106. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_backends.py +0 -0
  107. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_config.py +0 -0
  108. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_constants.py +0 -0
  109. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_evolver.py +0 -0
  110. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_fields.py +0 -0
  111. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_integrator.py +0 -0
  112. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_simulation.py +0 -0
  113. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tests/test_stencils.py +0 -0
  114. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tutorial_03_3d_lattice.png +0 -0
  115. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tutorial_07_3d_lattice.png +0 -0
  116. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tutorial_08_3d_lattice.png +0 -0
  117. {lfm_physics-0.2.2 → lfm_physics-0.4.0}/tutorial_12_3d_lattice.png +0 -0
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: Bug Report
3
+ about: Report a bug in lfm-physics
4
+ title: "[BUG] "
5
+ labels: bug
6
+ assignees: ""
7
+ ---
8
+
9
+ **Describe the bug**
10
+ A clear description of what the bug is.
11
+
12
+ **To Reproduce**
13
+ ```python
14
+ import lfm
15
+
16
+ config = lfm.SimulationConfig(grid_size=64)
17
+ sim = lfm.Simulation(config)
18
+ # ... steps to reproduce
19
+ ```
20
+
21
+ **Expected behavior**
22
+ What you expected to happen.
23
+
24
+ **Environment**
25
+ - OS: [e.g. Windows 11, Ubuntu 22.04]
26
+ - Python: [e.g. 3.12]
27
+ - lfm-physics version: [e.g. 0.3.0]
28
+ - GPU (if applicable): [e.g. RTX 4060]
29
+ - CuPy version (if applicable): [e.g. 13.0]
30
+
31
+ **Additional context**
32
+ Any other context — stack traces, screenshots, etc.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Feature Request
3
+ about: Suggest a new physics module or enhancement
4
+ title: "[FEATURE] "
5
+ labels: enhancement
6
+ assignees: ""
7
+ ---
8
+
9
+ **What physics / feature is missing?**
10
+ Describe the capability you need.
11
+
12
+ **Use case**
13
+ What simulation or analysis would this enable?
14
+
15
+ **Proposed API**
16
+ ```python
17
+ import lfm
18
+
19
+ # How you'd like to use it:
20
+ result = lfm.some_new_function(...)
21
+ ```
22
+
23
+ **References**
24
+ Links to relevant LFM papers, equations, or external references.
@@ -0,0 +1,19 @@
1
+ ## Summary
2
+
3
+ Brief description of the change and why it's needed.
4
+
5
+ ## Changes
6
+
7
+ -
8
+
9
+ ## Testing
10
+
11
+ - [ ] All existing tests pass (`pytest tests/`)
12
+ - [ ] New tests added for new functionality
13
+ - [ ] Tested with GPU backend (if kernel changes)
14
+
15
+ ## Checklist
16
+
17
+ - [ ] Code follows project style (run `ruff check`)
18
+ - [ ] Docstrings added for public functions
19
+ - [ ] CHANGELOG.md updated
@@ -0,0 +1,20 @@
1
+ # Pre-commit hooks for lfm-physics
2
+ # Install: pip install pre-commit && pre-commit install
3
+ # Run all: pre-commit run --all-files
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks
6
+ rev: v4.6.0
7
+ hooks:
8
+ - id: trailing-whitespace
9
+ - id: end-of-file-fixer
10
+ - id: check-yaml
11
+ - id: check-toml
12
+ - id: check-merge-conflict
13
+ - id: debug-statements
14
+
15
+ - repo: https://github.com/astral-sh/ruff-pre-commit
16
+ rev: v0.4.8
17
+ hooks:
18
+ - id: ruff # lint
19
+ args: [--fix]
20
+ - id: ruff-format # format
@@ -4,6 +4,47 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
6
6
 
7
+ ## [0.4.0] - 2026-03-22
8
+
9
+ ### Added
10
+ - **Metric analysis** (`lfm.analysis.metric`): `effective_metric_00`, `metric_perturbation`, `time_dilation_factor`, `gravitational_potential`, `schwarzschild_chi` — extract spacetime geometry from χ field
11
+ - **Phase/charge analysis** (`lfm.analysis.phase`): `phase_field`, `charge_density`, `phase_coherence`, `coulomb_interaction_energy` — electromagnetic observables from complex fields
12
+ - **Angular momentum** (`lfm.analysis.angular_momentum`): `angular_momentum_density`, `total_angular_momentum`, `precession_rate` — orbital and spin analysis
13
+ - **Boosted solitons** (`lfm.fields.boosted`): `boosted_soliton` — momentum-encoded solitons with phase gradient (complex) or time-derivative kick (real) for scattering experiments
14
+ - **2D parameter sweeps** (`lfm.sweep`): `sweep_2d()` — run all (v1, v2) combinations and collect metrics
15
+ - **GitHub governance**: issue/PR templates, SECURITY.md, CODE_OF_CONDUCT.md
16
+ - **Pre-commit hooks**: `.pre-commit-config.yaml` with ruff linting and formatting
17
+ - **Docs build**: `docs/Makefile` and `docs/make.bat` for local Sphinx builds
18
+ - **Coverage config**: `pyproject.toml` [tool.coverage.*] sections (excluding viz/ and kernel_source.py)
19
+
20
+ ### Fixed
21
+ - **GPU NaN bug**: CUDA color kernel threshold mismatch (`psi_sq_total > 1e-30f` → `total_sq > 1e-30f`) that caused FTZ underflow to 0/0=NaN
22
+ - **GPU boundary masking**: Added missing Psi boundary masking in color CUDA kernel
23
+ - **Color variance RuntimeWarning**: Safe denominator pattern to avoid division-by-zero warning on zero-field input
24
+ - **Schwarzschild chi RuntimeWarning**: Compute `np.where(r > r_s, 1.0 - r_s/r, 0.0)` before `np.sqrt` to avoid invalid-value warning
25
+
26
+ ### Improved
27
+ - Test suite: 202 → **307 tests** (all passing)
28
+ - Coverage: 64% → **91%** (excluding viz/ and CUDA kernel strings)
29
+ - Docs version: aligned at 0.4.0 across pyproject.toml, lfm/__init__.py, docs/conf.py
30
+
31
+ ## [0.3.0] - 2026-03-21
32
+
33
+ ### Added
34
+ - **Visualisation module** (`lfm.viz`): 10 plotting functions for rapid exploration
35
+ - `plot_slice`, `plot_three_slices`, `plot_chi_histogram` — 2D field slices
36
+ - `plot_evolution`, `plot_energy_components` — time-series dashboards
37
+ - `plot_radial_profile` — χ(r) with 1/r reference overlay
38
+ - `plot_isosurface` — 3D voxel rendering of χ wells/voids
39
+ - `plot_power_spectrum` — Fourier P(k) visualisation
40
+ - `plot_trajectories` — peak motion scatter plots
41
+ - `plot_sweep` — parameter sweep line plots
42
+ - **Power spectrum analyser** (`lfm.analysis.spectrum`): `power_spectrum()` — radially-averaged FFT P(k) for any 3D field
43
+ - **Particle tracker** (`lfm.analysis.tracker`): `track_peaks()` and `flatten_trajectories()` — follow energy-density maxima across timesteps
44
+ - **Parameter sweep runner** (`lfm.sweep`): `sweep()` — run a batch of simulations varying one parameter, collect metrics
45
+ - **Docs**: `docs/troubleshooting.md` — common errors (NaN, CFL, slow, imports) with fixes
46
+ - **Docs**: `docs/primer.md` — "LFM in Five Minutes" physics primer
47
+
7
48
  ## [0.2.1] - 2026-03-20
8
49
 
9
50
  ### Added
@@ -0,0 +1,31 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as contributors and maintainers pledge to make participation in our
6
+ project a harassment-free experience for everyone.
7
+
8
+ ## Our Standards
9
+
10
+ Examples of behavior that contributes to a positive environment:
11
+
12
+ * Using welcoming and inclusive language
13
+ * Being respectful of differing viewpoints
14
+ * Gracefully accepting constructive criticism
15
+ * Focusing on what is best for the community
16
+
17
+ Examples of unacceptable behavior:
18
+
19
+ * Trolling, insulting/derogatory comments, and personal attacks
20
+ * Public or private harassment
21
+ * Publishing others' private information without permission
22
+
23
+ ## Enforcement
24
+
25
+ Instances of unacceptable behavior may be reported to the project maintainers.
26
+ All complaints will be reviewed and investigated promptly and fairly.
27
+
28
+ ## Attribution
29
+
30
+ This Code of Conduct is adapted from the
31
+ [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lfm-physics
3
- Version: 0.2.2
3
+ Version: 0.4.0
4
4
  Summary: Lattice Field Medium physics simulation library
5
5
  Project-URL: Homepage, https://github.com/gpartin/lfm-physics
6
6
  Project-URL: Repository, https://github.com/gpartin/lfm-physics
@@ -214,6 +214,56 @@ fc = lfm.color_variance(psi_real_color, psi_imag_color)
214
214
 
215
215
  # Confinement proxy — χ line integral between peaks
216
216
  proxy = lfm.confinement_proxy(sim.chi, pos_a, pos_b)
217
+
218
+ # Fourier power spectrum P(k) of any 3D field
219
+ spec = lfm.power_spectrum(sim.chi, bins=50)
220
+ # spec['k'], spec['power']
221
+
222
+ # Track energy peaks across a run
223
+ trajectories = lfm.track_peaks(sim, steps=5000, interval=200, n_peaks=3)
224
+ ```
225
+
226
+ ## Parameter Sweeps
227
+
228
+ ```python
229
+ # Sweep amplitude from 2 to 10 and record chi_min at each value
230
+ config = lfm.SimulationConfig(grid_size=32)
231
+ results = lfm.sweep(config, param="amplitude", values=[2, 4, 6, 8, 10],
232
+ steps=3000, metric_names=["chi_min", "well_fraction"])
233
+ for r in results:
234
+ print(f"amp={r['amplitude']:.0f} chi_min={r['chi_min']:.2f}")
235
+ ```
236
+
237
+ ## Visualisation *(New in 0.3.0)*
238
+
239
+ Install with: `pip install "lfm-physics[viz]"`
240
+
241
+ ```python
242
+ from lfm.viz import (
243
+ plot_slice, # 2D slice through a 3D field
244
+ plot_three_slices, # XY + XZ + YZ panels
245
+ plot_chi_histogram, # distribution of χ values
246
+ plot_evolution, # time-series of metrics
247
+ plot_energy_components, # stacked kinetic / gradient / potential
248
+ plot_radial_profile, # χ(r) with 1/r reference overlay
249
+ plot_isosurface, # 3D voxel rendering
250
+ plot_power_spectrum, # P(k) from Fourier analysis
251
+ plot_trajectories, # peak motion in x-y / x-z / y-z
252
+ plot_sweep, # sweep results line plot
253
+ )
254
+
255
+ # Example: slice through the chi field at z = 32
256
+ fig, ax = plot_slice(sim.chi, axis=2, index=32, title="χ mid-plane")
257
+ fig.savefig("chi_slice.png")
258
+
259
+ # Three-panel overview
260
+ fig = plot_three_slices(sim.chi, title="χ field")
261
+
262
+ # Time evolution dashboard
263
+ fig = plot_evolution(sim.history)
264
+
265
+ # Radial profile with 1/r fit
266
+ fig, ax = plot_radial_profile(sim.chi, center=(32,32,32))
217
267
  ```
218
268
 
219
269
  ## Checkpoints & Units
@@ -172,6 +172,56 @@ fc = lfm.color_variance(psi_real_color, psi_imag_color)
172
172
 
173
173
  # Confinement proxy — χ line integral between peaks
174
174
  proxy = lfm.confinement_proxy(sim.chi, pos_a, pos_b)
175
+
176
+ # Fourier power spectrum P(k) of any 3D field
177
+ spec = lfm.power_spectrum(sim.chi, bins=50)
178
+ # spec['k'], spec['power']
179
+
180
+ # Track energy peaks across a run
181
+ trajectories = lfm.track_peaks(sim, steps=5000, interval=200, n_peaks=3)
182
+ ```
183
+
184
+ ## Parameter Sweeps
185
+
186
+ ```python
187
+ # Sweep amplitude from 2 to 10 and record chi_min at each value
188
+ config = lfm.SimulationConfig(grid_size=32)
189
+ results = lfm.sweep(config, param="amplitude", values=[2, 4, 6, 8, 10],
190
+ steps=3000, metric_names=["chi_min", "well_fraction"])
191
+ for r in results:
192
+ print(f"amp={r['amplitude']:.0f} chi_min={r['chi_min']:.2f}")
193
+ ```
194
+
195
+ ## Visualisation *(New in 0.3.0)*
196
+
197
+ Install with: `pip install "lfm-physics[viz]"`
198
+
199
+ ```python
200
+ from lfm.viz import (
201
+ plot_slice, # 2D slice through a 3D field
202
+ plot_three_slices, # XY + XZ + YZ panels
203
+ plot_chi_histogram, # distribution of χ values
204
+ plot_evolution, # time-series of metrics
205
+ plot_energy_components, # stacked kinetic / gradient / potential
206
+ plot_radial_profile, # χ(r) with 1/r reference overlay
207
+ plot_isosurface, # 3D voxel rendering
208
+ plot_power_spectrum, # P(k) from Fourier analysis
209
+ plot_trajectories, # peak motion in x-y / x-z / y-z
210
+ plot_sweep, # sweep results line plot
211
+ )
212
+
213
+ # Example: slice through the chi field at z = 32
214
+ fig, ax = plot_slice(sim.chi, axis=2, index=32, title="χ mid-plane")
215
+ fig.savefig("chi_slice.png")
216
+
217
+ # Three-panel overview
218
+ fig = plot_three_slices(sim.chi, title="χ field")
219
+
220
+ # Time evolution dashboard
221
+ fig = plot_evolution(sim.history)
222
+
223
+ # Radial profile with 1/r fit
224
+ fig, ax = plot_radial_profile(sim.chi, center=(32,32,32))
175
225
  ```
176
226
 
177
227
  ## Checkpoints & Units
@@ -0,0 +1,21 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported |
6
+ | ------- | ------------------ |
7
+ | 0.3.x | :white_check_mark: |
8
+ | < 0.3 | :x: |
9
+
10
+ ## Reporting a Vulnerability
11
+
12
+ If you discover a security vulnerability, please report it responsibly:
13
+
14
+ 1. **Do not** open a public issue.
15
+ 2. Email the maintainer directly with details.
16
+ 3. Allow reasonable time for a fix before public disclosure.
17
+
18
+ lfm-physics is a scientific simulation library with no network services,
19
+ authentication, or user data handling. Security concerns are limited to
20
+ arbitrary code execution via pickle/eval (we use only numpy `.npz` for
21
+ checkpoints, which is safe).
@@ -0,0 +1,23 @@
1
+ # Minimal makefile for Sphinx documentation
2
+ SPHINXOPTS ?=
3
+ SPHINXBUILD ?= sphinx-build
4
+ SOURCEDIR = .
5
+ BUILDDIR = _build
6
+
7
+ .PHONY: help html clean livehtml
8
+
9
+ help:
10
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS)
11
+
12
+ html:
13
+ @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS)
14
+
15
+ clean:
16
+ @rm -rf $(BUILDDIR)
17
+
18
+ livehtml:
19
+ sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)/html" $(SPHINXOPTS)
20
+
21
+ # Catch-all target
22
+ %:
23
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS)
@@ -12,7 +12,7 @@ sys.path.insert(0, os.path.abspath(".."))
12
12
  project = "lfm-physics"
13
13
  copyright = "2026, Greg Partin"
14
14
  author = "Greg Partin"
15
- release = "0.1.3"
15
+ release = "0.4.0"
16
16
 
17
17
  # -- General configuration ---------------------------------------------------
18
18
 
@@ -0,0 +1,26 @@
1
+ @ECHO OFF
2
+ pushd %~dp0
3
+
4
+ REM Minimal Windows batch file for Sphinx documentation
5
+
6
+ if "%SPHINXBUILD%" == "" (
7
+ set SPHINXBUILD=sphinx-build
8
+ )
9
+ set SOURCEDIR=.
10
+ set BUILDDIR=_build
11
+
12
+ if "%1" == "" goto help
13
+ if "%1" == "clean" goto clean
14
+
15
+ %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
16
+ goto end
17
+
18
+ :clean
19
+ rmdir /s /q %BUILDDIR% 2>NUL
20
+ goto end
21
+
22
+ :help
23
+ %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
24
+
25
+ :end
26
+ popd
@@ -0,0 +1,117 @@
1
+ # LFM in Five Minutes
2
+
3
+ A minimal introduction to the physics behind the `lfm-physics` library.
4
+
5
+ ---
6
+
7
+ ## One Sentence
8
+
9
+ > The universe is a cubic lattice where every point stores two numbers —
10
+ > a wave amplitude **Ψ** and a local stiffness **χ** — and they evolve
11
+ > by two coupled wave equations.
12
+
13
+ ---
14
+
15
+ ## The Two Fields
16
+
17
+ | Symbol | Name | Physical meaning |
18
+ |--------|------|------------------|
19
+ | **Ψ** | Wave field | Energy / matter at each lattice point |
20
+ | **χ** | Chi field | Stiffness of the lattice at each point |
21
+
22
+ Empty space has χ = 19 everywhere. Where energy concentrates, χ drops
23
+ below 19, forming a potential well — what we call *gravity*.
24
+
25
+ ---
26
+
27
+ ## The Two Equations
28
+
29
+ **GOV-01 — Wave Equation** (how Ψ evolves):
30
+
31
+ ```
32
+ Ψⁿ⁺¹ = 2Ψⁿ − Ψⁿ⁻¹ + Δt²[c²∇²Ψⁿ − (χⁿ)²Ψⁿ]
33
+ ```
34
+
35
+ Energy propagates through the lattice. The `χ²Ψ` term means waves
36
+ oscillate faster where χ is high (empty space) and slower where χ is low
37
+ (near matter). This is how gravity bends light.
38
+
39
+ **GOV-02 — Chi Equation** (how χ evolves):
40
+
41
+ ```
42
+ χⁿ⁺¹ = 2χⁿ − χⁿ⁻¹ + Δt²[c²∇²χⁿ − κ(|Ψⁿ|² − E₀²)]
43
+ ```
44
+
45
+ Energy density |Ψ|² pushes χ down — matter curves spacetime. The
46
+ coupling κ = 1/63 is derived from the lattice geometry.
47
+
48
+ **That's it.** These two update rules, applied at every lattice point
49
+ every timestep, produce gravity, waves, dark matter, expansion, and more.
50
+
51
+ ---
52
+
53
+ ## Why 19?
54
+
55
+ The number 19 comes from counting non-propagating modes on a 3D cubic
56
+ lattice:
57
+
58
+ | Mode type | Count | k-vectors |
59
+ |-----------|-------|-----------|
60
+ | Centre | 1 | (0,0,0) |
61
+ | Faces | 6 | (±1,0,0), (0,±1,0), (0,0,±1) |
62
+ | Edges | 12 | (±1,±1,0), etc. |
63
+ | **Total** | **19** | **= χ₀** |
64
+
65
+ The remaining 8 corner modes (±1,±1,±1) are propagating — identifying
66
+ them with gluons gives N_gluons = 8.
67
+
68
+ From χ₀ = 19 alone, LFM derives 40+ physical constants to high accuracy.
69
+
70
+ ---
71
+
72
+ ## What Emerges
73
+
74
+ | Phenomenon | How it arises |
75
+ |------------|---------------|
76
+ | **Gravity** | Energy (|Ψ|²) dips χ → potential well → attraction |
77
+ | **Dark matter** | χ wells persist after matter moves away (memory) |
78
+ | **Electromagnetism** | Phase of complex Ψ = charge; interference = Coulomb |
79
+ | **Expansion** | Voids evacuate → χ rises → photons slow down |
80
+ | **Particles** | Standing waves trapped in self-consistent χ wells |
81
+ | **Atoms** | Nuclear χ well + bound electron eigenmodes |
82
+
83
+ ---
84
+
85
+ ## Quick Start
86
+
87
+ ```python
88
+ import lfm
89
+
90
+ # Create a 64-cell cubic universe
91
+ config = lfm.SimulationConfig(grid_size=64)
92
+ sim = lfm.Simulation(config)
93
+
94
+ # Drop a soliton — a localized energy concentration
95
+ sim.place_soliton((32, 32, 32), amplitude=6.0)
96
+
97
+ # Evolve for 1000 timesteps
98
+ sim.run(steps=1000)
99
+
100
+ # Look at what happened
101
+ print(f"χ_min = {sim.chi.min():.2f}") # Should be < 19 (gravity!)
102
+ ```
103
+
104
+ See the [examples/](../examples/) directory for 14 runnable demos covering
105
+ gravity, electromagnetism, atoms, orbits, cosmology, and more.
106
+
107
+ ---
108
+
109
+ ## Going Deeper
110
+
111
+ | Topic | Resource |
112
+ |-------|----------|
113
+ | Full API reference | `help(lfm.Simulation)` |
114
+ | All 14 examples | [examples/](../examples/) |
115
+ | Visualization | `from lfm.viz import plot_slice` |
116
+ | Parameter sweeps | `from lfm import sweep` |
117
+ | Common errors | [troubleshooting.md](troubleshooting.md) |
@@ -0,0 +1,159 @@
1
+ # Troubleshooting
2
+
3
+ Common problems and how to fix them.
4
+
5
+ ---
6
+
7
+ ## My simulation gives NaN
8
+
9
+ **Cause:** The wave amplitude is too large for the grid, causing the
10
+ leapfrog integrator to diverge.
11
+
12
+ **Fix:**
13
+ ```python
14
+ # Lower the amplitude
15
+ sim.place_soliton((32, 32, 32), amplitude=4.0) # instead of 12.0
16
+
17
+ # Or use a larger grid (more room to spread energy)
18
+ config = lfm.SimulationConfig(grid_size=128) # instead of 32
19
+ ```
20
+
21
+ **Rule of thumb:** Keep amplitude below ~8 for `grid_size=32`, below ~12
22
+ for `grid_size=64`. When in doubt, start small and increase.
23
+
24
+ ---
25
+
26
+ ## Energy diverges over time
27
+
28
+ **Cause:** The timestep `dt` exceeds the CFL stability limit.
29
+
30
+ **Fix:** Use the default `dt=0.02` — it satisfies the CFL condition for
31
+ all standard setups. If you've changed `dt` manually:
32
+
33
+ ```python
34
+ # Safe: dt = 0.02 (default, well inside CFL)
35
+ config = lfm.SimulationConfig(dt=0.02)
36
+
37
+ # The CFL limit for the 19-point stencil with χ₀=19:
38
+ # dt < 1/sqrt(16c²/(3·Δx²) + χ₀²) ≈ 0.104 (for Δx=c=1)
39
+ # Our default 0.02 gives ~5× safety margin.
40
+ ```
41
+
42
+ ---
43
+
44
+ ## χ goes negative
45
+
46
+ **This is not a bug.** In extreme-gravity scenarios (very high amplitude
47
+ or dense clusters), χ can dip below zero. This is the LFM equivalent of
48
+ a black hole interior.
49
+
50
+ **If you want to prevent it:** Enable the Mexican-hat self-interaction,
51
+ which creates a stable second vacuum at −χ₀:
52
+
53
+ ```python
54
+ config = lfm.SimulationConfig(
55
+ lambda_self=lfm.LAMBDA_H, # 4/31 ≈ 0.129
56
+ )
57
+ ```
58
+
59
+ **If you don't care about extreme gravity:** Use gravity-only mode
60
+ (default `lambda_self=0.0`) and lower the amplitude.
61
+
62
+ ---
63
+
64
+ ## Everything collapses into one blob
65
+
66
+ **Cause:** With periodic boundaries and enough energy, gravity always
67
+ wins — everything falls toward the centre of mass. This is correct
68
+ physics for a closed universe.
69
+
70
+ **Fix:** Use frozen boundaries (the default) to simulate an open region
71
+ embedded in the vacuum:
72
+
73
+ ```python
74
+ config = lfm.SimulationConfig(
75
+ boundary_type=lfm.BoundaryType.FROZEN, # default
76
+ )
77
+ ```
78
+
79
+ Frozen boundaries hold χ = 19 at the edges, representing infinite empty
80
+ space beyond the simulation box.
81
+
82
+ ---
83
+
84
+ ## Simulation is very slow
85
+
86
+ **Check 1 — Are you using GPU?**
87
+ ```python
88
+ print(lfm.gpu_available()) # True if CuPy + CUDA detected
89
+
90
+ # Force GPU
91
+ sim = lfm.Simulation(config, backend="gpu")
92
+ ```
93
+
94
+ Install GPU support:
95
+ ```bash
96
+ pip install "lfm-physics[gpu]"
97
+ ```
98
+
99
+ **Check 2 — Is your grid too large for CPU?**
100
+
101
+ | Grid size | Cells | CPU time/step | GPU time/step |
102
+ |-----------|-----------|---------------|---------------|
103
+ | 32³ | 32 K | ~0.3 ms | ~0.02 ms |
104
+ | 64³ | 262 K | ~3 ms | ~0.05 ms |
105
+ | 128³ | 2.1 M | ~30 ms | ~0.3 ms |
106
+ | 256³ | 16.8 M | ~300 ms | ~7 ms |
107
+
108
+ For grid_size ≥ 128 on CPU, expect minutes for long runs. GPU gives
109
+ 50–200× speedup.
110
+
111
+ ---
112
+
113
+ ## ImportError: matplotlib not found
114
+
115
+ The visualization module (`lfm.viz`) requires matplotlib:
116
+
117
+ ```bash
118
+ pip install "lfm-physics[viz]"
119
+ ```
120
+
121
+ Or install everything:
122
+ ```bash
123
+ pip install "lfm-physics[all]"
124
+ ```
125
+
126
+ ---
127
+
128
+ ## How do I save and resume a run?
129
+
130
+ ```python
131
+ # Save
132
+ sim.save_checkpoint("my_run.npz")
133
+
134
+ # Resume later
135
+ sim2 = lfm.Simulation.load_checkpoint("my_run.npz")
136
+ sim2.run(steps=5000) # continues where it left off
137
+ ```
138
+
139
+ Checkpoints preserve the full simulation state: fields, config, step
140
+ count, and metric history.
141
+
142
+ ---
143
+
144
+ ## Which field level should I use?
145
+
146
+ | I want to simulate... | Field level |
147
+ |-----------------------------------|-----------------------|
148
+ | Gravity, dark matter, cosmology | `FieldLevel.REAL` |
149
+ | Electromagnetism, charged particles | `FieldLevel.COMPLEX` |
150
+ | Strong force, color confinement | `FieldLevel.COLOR` |
151
+
152
+ ```python
153
+ config = lfm.SimulationConfig(
154
+ field_level=lfm.FieldLevel.COMPLEX, # for EM
155
+ )
156
+ ```
157
+
158
+ Start with REAL (simplest, fastest). Upgrade only when your physics
159
+ requires it.