Merge Conflict Resolution Guide¶
How to resolve merge conflicts when bringing main into a long-running
feature branch. This guide provides general conflict resolution
principles (§1–§2), a catalog of known restructuring PRs with
their specific conflict patterns (§3), and universal verification
gates (§4).
When a new restructuring PR lands in main, add an entry to §3
following the template in §6.
1. General principles¶
1.1 Diagnose before acting¶
Every conflict traces back to one of: - Files moved or renamed on one branch, modified on the other. - Files added on one branch at paths that don't exist on the other. - Files deleted on one branch, modified on the other. - Source lists or config blocks replaced wholesale.
Identify which pattern applies before choosing a resolution strategy.
1.2 Resolve toward the forward-looking layout¶
When one side represents a restructuring and the other contains incremental changes, resolve toward the restructured layout and transplant the incremental changes into it. Do not revert restructuring to avoid conflicts.
1.3 Verification is part of resolution¶
Git reporting zero unmerged files is not enough. Run the universal verification gates in §4 after every merge. Add PR-specific gates when adding a new entry to §3.
2. Quick diagnosis¶
# What conflicted and what type?
git status --short | grep -E '^UU|^UA|^AU|^DU|^UD|^AA|^DD'
# Inspect the three merge stages (1=base, 2=ours, 3=theirs)
git ls-files -u
# Diff our side vs theirs for a specific file
git diff HEAD...MERGE_HEAD -- <file>
Stage codes:
| Code | Meaning | Typical action |
|---|---|---|
UU |
Both modified | Merge contents manually |
UA |
Added by main, absent here | git add after checking for path moves (§3) |
UD |
Deleted by main, modified here | git rm if the deletion is legitimate |
3. Known restructuring PRs¶
Each entry documents the layout changes, conflict patterns, and verification gates specific to one restructuring PR. When a new restructuring PR lands, add a new subsection here.
Template: see §6.
3.1 PR #88 — Buildsystem Maintenance¶
What changed:
| Change | Before | After |
|---|---|---|
| C++ tests moved | python/tests/cpp/test_*.cpp |
novaphy/tests/test_*.cpp |
| C++ test build config moved | python/tests/CMakeLists.txt |
novaphy/tests/CMakeLists.txt |
| Python tests flattened | python/tests/python/test_*.py |
python/tests/test_*.py |
| CMake compat moved | compat/ |
cmake/compat/ |
| Demos installed as subpackage | (no __init__.py) |
python/demos/__init__.py → novaphy.demos |
Conflict patterns:
Pattern A: CMakeLists.txt source list replaced¶
Cause: main replaced VBD sources (cpu/vbd_*.cpp + vbd_cuda/)
with a new structure (solver_vbd.cpp + rigid/ + soft/).
Symptom: UU conflict in novaphy/src/dynamics/vbd/CMakeLists.txt
with two completely different source lists.
Fix: Take main's version in full (it's a replacement, not an addition).
git checkout --theirs novaphy/src/dynamics/vbd/CMakeLists.txt
git add novaphy/src/dynamics/vbd/CMakeLists.txt
Pattern B: File renamed here + modified on main¶
Cause: We moved a file (e.g. python/tests/CMakeLists.txt →
novaphy/tests/CMakeLists.txt) and main modified the original.
Symptom: UU conflict with Git markers referencing two different
file paths and different internal conventions (e.g. cpp/ prefix).
Fix: Keep our file location. Port main's logical changes (new
files, new link deps) into our path convention.
Pattern C: Test path depth wrong (parents[N] off by one)¶
Cause: Main's tests compute REPO_ROOT via
Path(__file__).resolve().parents[3] — correct for the old
python/tests/python/ depth, overshooting after flattening.
Symptom: FileNotFoundError with paths like
/home/user/src/python/... (repo name missing).
Fix: Reduce parents[N] by 1 for every test file that was moved:
| Move | Old | New |
|---|---|---|
python/tests/python/ → python/tests/ (REPO_ROOT) |
parents[3] |
parents[2] |
python/tests/python/ → python/tests/ (module loader) |
parents[2] |
parents[1] |
Pattern D: Main added files at old paths¶
Cause: main added files under python/tests/cpp/ or compat/
— directories we moved or deleted.
Symptom: UA conflicts at old paths.
Fix: Move them:
| Main added at | Move to |
|---|---|
python/tests/cpp/test_new.cpp |
novaphy/tests/test_new.cpp |
python/tests/python/test_new.py |
python/tests/test_new.py |
compat/new_file.cmake |
cmake/compat/new_file.cmake |
PR-specific verification¶
In addition to the universal gates in §4, verify:
3.2 PR #143 — Demo and pytest cleanup¶
What changed:
| Change | Before | After |
|---|---|---|
| LBM robot demos renamed | python/demos/demo_lbm_robot_pyvista.py, python/demos/demo_lbm_robot_gripper_pyvista.py |
python/demos/demo_lbm_robot.py, python/demos/demo_lbm_robot_gripper.py |
| LBM examples consolidated | python/demos/demo_lbm_immersed_bodies.py, python/demos/demo_lbm_pyvista_volume.py |
python/demos/_lbm_scene.py, python/demos/demo_lbm_volume.py |
| Featherstone rope consolidated | python/demos/featherstone/demo_fs_rope.py |
python/demos/featherstone/_rope_scene.py, python/demos/featherstone/demo_fs_pgs_rope.py |
| Legacy Polyscope fluid demos removed | python/demos/demo_ball_in_water.py, python/demos/demo_fluid_box.py |
python/demos/demo_fluid_coupling.py, python/demos/demo_dam_break.py |
| Legacy Polyscope stress demo removed | python/demos/demo_pyramids_numerous.py |
python/demos/featherstone/demo_fs_pgs_pyramids_numerous.py |
| Legacy PPO pendulum demo removed | python/demos/demoSim_ppo_inverted_pendulum.py |
Physics-only supported-viewer example: python/demos/xpbd/demo_xpbd_pendulum.py |
| Redundant XPBD demo removed | python/demos/demo_newton_xpbd_pyramid.py |
python/demos/demo_pyramid_ball.py and python/tests/test_xpbd_demos.py |
| Redundant IK utilities removed | python/demos/ik_arm_demo/benchmark.py, python/demos/ik_arm_demo/self_check.py |
python/demos/ik_arm_demo/benchmark_compare.py and python/tests/test_ik_demo.py |
| Standalone viewers retired | python/novaphy/viz_moderngl.py, python/novaphy/viz_pyvista.py |
python/novaphy/viewer/ and its ViewerGL/ViewerNull interfaces |
| Ad-hoc benchmark removed | python/tests/benchmark_rigid_1000.py |
python/demos/demo_performance_monitor.py and benchmark skill |
| Demo runtime tests migrated | python/tests/test_demo_simulate_loop_alignment.py |
python/tests/test_demo_runtime.py |
| Viewer implementation tests migrated | python/tests/test_viz_moderngl.py |
Split python/tests/test_viewer_* modules |
| Viewer tests split | python/tests/test_viewer_api.py |
python/tests/_viewer_test_support.py plus ten python/tests/test_viewer_* domain modules |
| Container tests split | python/tests/test_newton_container_contract.py |
python/tests/_newton_container_support.py plus eight python/tests/test_newton_* contract modules |
| Negative API contracts migrated | python/tests/test_no_compat_layer.py |
python/tests/test_newton_model_contract.py |
| URDF audit migrated | python/tests/test_newton_basic_urdf_audit.py |
python/tests/test_urdf_import_metadata.py |
| Solver config tests migrated | python/tests/test_solver_config_api.py |
python/tests/test_newton_signature_alignment.py plus solver-specific suites |
| Featherstone direct tests migrated | python/tests/test_solver_featherstone_direct.py |
python/tests/test_solver_featherstone_newton.py |
| XPBD demo tests migrated | python/tests/test_newton_xpbd_demo.py |
python/tests/test_xpbd_demos.py |
| VBD tests split | python/tests/test_solver_vbd.py, python/tests/test_solver_vbd_cuda.py |
Two support modules plus six CPU and four CUDA domain modules |
| MuJoCo tests split | python/tests/test_solver_mujoco_native.py |
python/tests/_solver_mujoco_support.py, python/tests/test_solver_mujoco_config.py, python/tests/test_solver_mujoco_dynamics.py |
| Joint tests split | python/tests/test_joint_unification.py |
python/tests/test_joint_types.py, python/tests/test_joint_metadata.py, python/tests/test_joint_builder_api.py |
Conflict patterns:
Pattern A: Renamed LBM demo modified on another branch¶
Cause: The cleanup renamed the two ViewerGL robot demos to remove the
obsolete _pyvista suffix while another branch edited or imported the old
path.
Symptom: A modify/delete conflict at an old demo path, or a runtime
ModuleNotFoundError for a module ending in _pyvista.
Fix: Keep the new filename, transplant the other branch's logical change
into it, and update imports to novaphy.demos.demo_lbm_robot or
novaphy.demos.demo_lbm_robot_gripper. Do not recreate a compatibility
module at the old path.
Pattern B: Consolidated demo deleted on one side and modified on the other¶
Cause: Reusable scene construction moved out of the retired LBM immersed and Featherstone rope entry points before those duplicate entry points were deleted.
Symptom: A UD conflict in demo_lbm_immersed_bodies.py or
featherstone/demo_fs_rope.py.
Fix: Port renderer-independent LBM changes into _lbm_scene.py and rope
topology changes into featherstone/_rope_scene.py. Port user-facing runtime
changes into demo_lbm_volume.py or demo_fs_pgs_rope.py, then keep the old
entry point deleted.
Pattern C: Monolithic pytest file modified after it was split¶
Cause: Another branch added a regression to one of the removed aggregate test modules.
Symptom: A modify/delete conflict, or a newly resurrected copy of
test_viewer_api.py, test_newton_container_contract.py,
test_solver_vbd.py, test_solver_vbd_cuda.py,
test_solver_mujoco_native.py, or test_joint_unification.py.
Fix: Move the individual test and any narrowly shared helper into the matching domain module. Use the test name and behavior, not its former line number, to choose among viewer lifecycle/backend/geometry/picking, container model/state/control/contact, VBD runtime/config/joint/particle/contact, MuJoCo config/dynamics, or joint type/metadata/builder modules. Keep the aggregate file deleted and confirm the test is collected exactly once.
Pattern D: Viewer extras conflict in pyproject.toml¶
Cause: This cleanup retired the PyVista volume extra, made the PyImgui GLFW integration explicit, and made the demo/development extras include the common viewer. Another branch may edit the same dependency table.
Symptom: A UU conflict in [project.optional-dependencies], a missing
imgui.integrations.glfw.GlfwRenderer, or an accidentally restored
viz-volume extra.
Fix: Preserve viz because the out-of-scope IPC demos still use it.
Preserve viewer with imgui[glfw] and pycollada for Collada/DAE mesh
loading, keep test with both trimesh and pycollada, keep examples and
dev dependent on viewer, and do not restore viz-volume or PyVista.
Pattern E: Retired demo or viewer modified on another branch¶
Cause: Another branch changed one of the legacy Polyscope demos, the standalone ModernGL/PyVista viewers, or the ad-hoc rigid benchmark after this cleanup selected a supported replacement.
Symptom: A UD conflict at one of the fully qualified demo, viewer, or
benchmark paths in the table above. Accepting the modified side resurrects a
retired dependency or duplicates a retained example.
Fix: Keep the old path deleted. Port physics behavior to the replacement
listed in the table and port rendering behavior to python/novaphy/viewer/.
The PPO pendulum has no feature-equivalent replacement; if its RL behavior is
still required, transplant that behavior into a new supported-viewer demo
instead of restoring its Polyscope entry point.
Pattern F: Migrated regression test modified on another branch¶
Cause: Another branch added assertions to a deleted API, solver, URDF, demo-runtime, XPBD-demo, or viewer-implementation test after its behavior was moved into a retained suite.
Symptom: A UD conflict in test_no_compat_layer.py,
test_solver_config_api.py, test_solver_featherstone_direct.py,
test_newton_basic_urdf_audit.py, test_demo_simulate_loop_alignment.py,
test_newton_xpbd_demo.py, or test_viz_moderngl.py.
Fix: Find the exact forward destination in the table, port the assertion there, and keep the old path deleted. Before resolving, search by the original test function name to avoid collecting the same regression twice.
PR-specific verification:
# Every deleted path in this restructuring must be cataloged above.
git diff --diff-filter=D --name-only main |
while IFS= read -r path; do
rg -F -q "\`$path\`" docs/guide/merge-conflict-resolution.md ||
{ echo "Undocumented deleted path: $path"; exit 1; }
done
# Removed entry points and aggregate tests must not return. This derives the
# complete list from the PR rather than maintaining a second partial list.
git diff --diff-filter=D --name-only main |
while IFS= read -r path; do test ! -e "$path" || exit 1; done
# All retained non-IPC demos use the common viewer path.
rg -n -i 'polyscope|SceneVisualizer|novaphy\.viz' \
python/demos --glob '!demo_ipc_*.py'
# Expected: zero results
# Retired PyVista surface stays absent.
rg -n -i 'pyvista|viz_pyvista|viz-volume' python pyproject.toml
# Expected: zero results
# Split suites and behavior migrations remain healthy.
pytest python/tests/ -v
3.3 2026-08-20 — MPM pytest subfolders under python/tests/python/¶
What changed: MPM-related pytest modules were classified into
subdirectories. Engine-wide tests remain in python/tests/test_*.py.
Shared gold/forensics helpers moved to python/tests/python/_lib/
(pyproject.toml pythonpath).
| Change | Before | After |
|---|---|---|
| Lite | python/tests/python/test_lite_mpm_*.py |
python/tests/python/lite/ |
| newton_ref demos | python/tests/python/test_newton_ref_mpm_*.py |
python/tests/python/newton_ref/ |
| Newton official / ring | python/tests/python/test_newton_official_*.py, test_newton_ring_*.py |
python/tests/python/newton_official/ |
| Implicit MPM / Route B core | python/tests/python/test_implicit_mpm_*.py |
python/tests/python/implicit_mpm/ |
| Perf (O1–O7) | python/tests/python/test_o*.py |
python/tests/python/perf/ |
| S4c contact / graph | python/tests/python/test_s4c_*.py, test_s42*.py, test_s43*.py |
python/tests/python/s4c/ |
| M3b | python/tests/python/test_m3b_*.py |
python/tests/python/multi_material/ |
| Scene CLI / snow / granular | python/tests/python/test_snow_*.py, test_granular_*.py, … |
python/tests/python/scene/ |
| Sand table schema (S0–S6) | python/tests/python/test_s0_*.py … test_s6_*.py |
python/tests/python/sand/ |
| Helpers | o4a_*_gold.py, _mpm_cuda_gs_forensics.py |
python/tests/python/_lib/ |
| Future official catalog ports | (did not exist) | python/tests/python/newton_official/test_implicit_mpm_newton_official_*.py |
Layout map: python/tests/python/README.md.
Conflict patterns:
Pattern A: Main added a test at the old flat path¶
Cause: main still creates python/tests/python/test_<name>.py.
Symptom: UA at the flat path, or two copies of the same file
(flat + subfolder).
Fix: git mv into the matching subfolder from the table above.
Do not leave a duplicate at the old path. Bump Path(__file__).parents[N]
by +1 (repo root is parents[4]; python/ is parents[3]).
Pattern B: parents[N] off by one after the move¶
Cause: Flat files used parents[3] for repo root. One extra
directory makes that python/ instead of the repo.
Symptom: FileNotFoundError resolving docs/ or python/demos/
from a test.
Fix: In the moved file, increment every parents[N] by 1.
Pattern C: Helper import from o4p_project_outside_gold import …¶
Cause: Gold modules left python/tests/python/ and now live in _lib/.
Symptom: ModuleNotFoundError: o4p_project_outside_gold (or
o4a_world_int3_active_map_gold, _mpm_cuda_gs_forensics).
Fix: Keep pythonpath = ["python/tests/python/_lib"] in
pyproject.toml. Do not copy helpers back to the flat directory.
PR-specific verification:
# No leftover flat test_*.py next to the subfolders
ls python/tests/python/test_*.py
# Expected: no such files (exit 2 / empty)
grep -n 'parents\[3\]' python/tests/python/*/*.py | head
# Repo-root lookups in subfolder tests should be parents[4], not [3]
3.4 four_scene11 PR preparation — MPM and upstream differentiable APIs¶
What changed:
| Change | Before | After |
|---|---|---|
| Installed MPM demo imports | demos.mpm.* plus source-path injection |
novaphy.demos.mpm.* |
| Installed diagnostic helpers | tools.mpm.* available only from the checkout |
novaphy.tools.mpm.*, included by wheel package mapping |
| Modified MPM Config regression | Deleted aggregate test_solver_config_api.py |
Existing test_newton_signature_alignment.py, using native nested MPM Config |
| Vendored dependency license in wheel | License only in source checkout | Root and Warp licenses included in wheel metadata |
| Shared CUDA helper lookup | Only $ORIGIN/novaphy-bundled-libs |
Also search sibling $ORIGIN for novaphy/lib helpers |
| NVIDIA driver dependency | Driver could be copied into wheel | Exclude host libcuda/NVML (and Windows counterparts); retain CUDA runtime dependencies |
Source directories stay under python/demos and python/tools; import names
follow their installed package names. Existing diagnostic data/probe requirements
are not removed by packaging their modules.
Conflict patterns:
- Bindings: retain MPM binder sources and calls together with upstream DIFF
binders and
NO_EXTRAS/IPO-off configuration. Close the VBD-only preprocessor guard before MPM declarations/calls; MPM does not depend on VBD being enabled. - Python exports: retain MPM symbol validation and upstream package-qualified
novaphy.nova/novaphy.diffregistration. Neither block replaces the other. - Pytest hook: mark MPM catalog/parity items before applying the optional VBD skip logic. Do not let an early VBD return suppress MPM categorization.
- Test migration: preserve PR #143's aggregate deletion and port only the MPM Config assertions to the current signature suite.
- Guide numbering: keep the upstream PR #143 catalog at 3.2 and the historical MPM subdirectory catalog at 3.3. Do not discard either catalog.
PR-specific verification:
Build/install the wheel into an isolated environment, then run from outside the
checkout (no source PYTHONPATH, no top-level demos/tools alias):
python -m pytest python/tests/test_mpm_installed_imports.py -q
python -m pytest python/tests/test_newton_signature_alignment.py -q
python -m novaphy.demos.mpm.newton_ref.demo_mpm_multi_material --help
The first test launches isolated subprocesses from temporary directories. Also
verify the wheel contains vendor/warp_native/LICENSE.md under its dist-info
licenses directory, verify loaded _core and libraries belong to the installed
wheel, and rerun the four scene startup checks after any binding/CUDA rebuild.
3.5 PR #154 — Native MPM integration overlapping the #151 snapshot¶
What changed: PR #151 and PR #154 imported related MPM snapshots on separate branches. Their Git merge base predates both imports, so Git reports add/add conflicts even where #154 contains the later implementation.
| Area | Earlier #151 snapshot | #154 integration |
|---|---|---|
| MPM runtime | four_scene11 | Native runtime with buffer/cache reuse and in-place grain stepping |
| Beam math | vendor/warp_native consumers |
novaphy/include/math/beam_native_math.cuh and native helpers |
| State continuation | particle history/contact | dense warmstart and ground-boundary snapshots |
| Grain rendering | original driver | native grain binding and render helpers |
Cause: Squashed snapshot imports hide shared source history from Git.
Symptom: Many add/add conflicts across MPM headers, implementations and
demos, plus overlapping state, binding and CMake registration edits.
Fix: Identify the historical snapshot before the upstream compatibility
patches. For this merge it is 30143868; #154 is 697e3181, and upstream
is 2949733f. Use that historical file as the three-way content base for
the overlapping files, then review against both real merge parents. Do not
select all upstream files merely because their squash commit is newer.
Preserve the upstream CPU-only build guards, StateFlags API, Windows output
encoding and solver/CI fixes. Keep the native MPM source lists and signatures
together. Package both native-origin attribution and the retained vendor
license; upstream still contains the vendored source and its license test.
Retain upstream pytest helper paths and marker registrations: the source-only
154 snapshot did not include #151's relocated test configuration. Adapt those¶
tests to caller-owned MPM state, float32 time steps, and the updated Grain CLI defaults. CUDA tests must require the compiled backend rather than relying on an older silent CPU fallback; keep their numerical assertions intact.
PR-specific verification:
pytest python/tests/test_mpm_reset_state_flags.py \
python/tests/test_solver_backend_info.py \
python/tests/test_mpm_installed_imports.py \
python/tests/test_mpm_cli_output_encoding.py -v
pytest python/tests/ -v
ctest --test-dir <build-dir> --output-on-failure
Use the freshly built installed extension. Validate CUDA scenes separately when a functioning NVIDIA driver is available; CPU success does not validate CUDA execution or graph replay.
3.6 PR #154 — Descriptive multi-material filenames and shared license notice¶
What changed: Replace the historical milestone label m3b with feature
names. The test directory stays at the same depth; Path.parents indices do
not change. Python helper symbols and legacy CLI flags remain compatible.
| Before | After |
|---|---|
docs/handoffs/m3b_demo_cli_reference.md |
docs/handoffs/multi_material_demo_cli_reference.md |
docs/handoffs/m3b_dt_substeps_sweep_results.md |
docs/handoffs/multi_material_dt_substeps_sweep_results.md |
docs/handoffs/mpm_m3b_cuda_impl.md |
docs/handoffs/mpm_multi_material_cuda_impl.md |
docs/handoffs/newton_mpm_port_novaphy_before_m3b_impl.md |
docs/handoffs/newton_mpm_port_novaphy_before_multi_material_impl.md |
python/demos/mpm/newton_ref/README_before_m3b_impl.md |
python/demos/mpm/newton_ref/README_before_multi_material_impl.md |
python/tests/python/implicit_mpm/test_implicit_mpm_newton_fem_cuda_m3b_l1.py |
python/tests/python/implicit_mpm/test_implicit_mpm_newton_fem_cuda_per_particle_materials.py |
python/tests/python/implicit_mpm/test_implicit_mpm_newton_fem_m3b_l1.py |
python/tests/python/implicit_mpm/test_implicit_mpm_newton_fem_per_particle_materials.py |
python/tests/python/m3b/test_m3b_cli_profile.py |
python/tests/python/multi_material/test_multi_material_cli_profile.py |
python/tests/python/m3b/test_m3b_dense_180k_emit.py |
python/tests/python/multi_material/test_multi_material_dense_180k_emit.py |
python/tests/python/m3b/test_m3b_internal_substeps.py |
python/tests/python/multi_material/test_multi_material_internal_substeps.py |
python/tests/python/m3b/test_m3b_isotropic_domain.py |
python/tests/python/multi_material/test_multi_material_isotropic_domain.py |
python/tests/python/m3b/test_m3b_kinematic_particle_floor.py |
python/tests/python/multi_material/test_multi_material_kinematic_particle_floor.py |
python/tests/python/m3b/test_m3b_newton_world_scale.py |
python/tests/python/multi_material/test_multi_material_newton_world_scale.py |
python/tests/python/m3b/test_m3b_rasterize_domain_box.py |
python/tests/python/multi_material/test_multi_material_rasterize_domain_box.py |
python/tests/python/m3b/test_m3b_sparse_allocate_cuda_soa_a.py |
python/tests/python/multi_material/test_multi_material_sparse_cuda_strain_storage.py |
python/tests/python/m3b/test_m3b_sparse_allocate_day5.py |
python/tests/python/multi_material/test_multi_material_sparse_active_remap.py |
python/tests/python/m3b/test_m3b_sparse_allocate_day6.py |
python/tests/python/multi_material/test_multi_material_sparse_strain_cpu.py |
python/tests/python/m3b/test_m3b_sparse_allocate_day7.py |
python/tests/python/multi_material/test_multi_material_sparse_cli_cache_invalidation.py |
python/tests/python/m3b/test_m3b_sparse_allocate_knife1_assemble.py |
python/tests/python/multi_material/test_multi_material_sparse_device_assemble.py |
python/tests/python/m3b/test_m3b_sparse_allocate_knife2_gs_pack.py |
python/tests/python/multi_material/test_multi_material_sparse_gauss_seidel_pack.py |
python/tests/python/m3b/test_m3b_sparse_allocate_knife3_ensure.py |
python/tests/python/multi_material/test_multi_material_sparse_buffer_capacity.py |
python/tests/python/m3b/test_m3b_sparse_allocate_knife4_fe_gate.py |
python/tests/python/multi_material/test_multi_material_sparse_elastic_writeback.py |
python/tests/python/m3b/test_m3b_world_meter_kernel.py |
python/tests/python/multi_material/test_multi_material_world_meter_kernel.py |
python/tests/python/m3b/test_m3b_world_voxel_cuda_rect.py |
python/tests/python/multi_material/test_multi_material_world_voxel_cuda_rect.py |
python/tests/python/m3b/test_m3b_world_voxel_dense_rect.py |
python/tests/python/multi_material/test_multi_material_world_voxel_dense_rect.py |
python/tests/python/m3b/test_m3b_world_voxel_grid.py |
python/tests/python/multi_material/test_multi_material_world_voxel_grid.py |
python/tests/python/newton_ref/test_newton_ref_mpm_multi_material_m3b.py |
python/tests/python/newton_ref/test_newton_ref_mpm_multi_material_per_particle.py |
python/tests/python/newton_ref/test_newton_ref_mpm_multi_material_m3b_cuda.py |
python/tests/python/newton_ref/test_newton_ref_mpm_multi_material_per_particle_cuda.py |
python/tools/mpm/m3b_dt_substeps_sweep.py |
python/tools/mpm/multi_material_dt_substeps_sweep.py |
LICENSES/ORIGINS.md |
THIRD_PARTY_NOTICES (concise provenance) |
LICENSES/warp-origin-LICENSE.md |
Existing identical vendor/warp_native/LICENSE.md |
Conflict patterns:
- Cause: another branch edits a renamed test/tool/doc. Symptom:
rename/modify or modify/delete conflict, or a duplicate test under
m3b/. Fix: port the changes to the matching destination above, remove the old path, and update full-path, bare-filename and module-import references. Keep historicalm3bPython identifiers where callers still use them. - Cause: another branch changes wheel notices. Symptom: old deleted
license paths reappear in
wheel.license-files. Fix: includeTHIRD_PARTY_NOTICESandvendor/warp_native/LICENSE.md; retain the root license. The vendor license is byte-identical to the removed duplicate.
PR-specific verification:
# No milestone filenames remain (historical names in this migration table are intentional).
git ls-files | rg -i m3b
# Expected: no output after staging.
python -m pytest python/tests/python/multi_material/ -q
python -m novaphy.demos.mpm.newton_ref.demo_mpm_multi_material --help
# Wheel metadata must include THIRD_PARTY_NOTICES and vendor/warp_native/LICENSE.md.
3.7 PR #154 — Rename the native strain finite-element backend¶
What changed: 85 backend headers/sources move together. The method name
strain_fem replaces the reference-project label in paths and C++ namespaces.
46 distinct basenames change (some occur as both headers and sources). Existing
public type/function/config names, Python exports, CLI flags and reference
scenarios retain their names and values.
| Before | After |
|---|---|
novaphy/include/fluid/mpm/implicit/backends/newton_fem/** |
novaphy/include/fluid/mpm/implicit/backends/strain_fem/** |
novaphy/src/fluid/mpm/implicit/backends/newton_fem/** |
novaphy/src/fluid/mpm/implicit/backends/strain_fem/** |
Backend basenames newton_fem_* |
strain_fem_* (same extension) |
Backend basenames implicit_mpm_newton_fem* |
implicit_mpm_strain_fem* (same extension) |
novaphy::mpm::implicit::newton_fem |
novaphy::mpm::implicit::strain_fem (including nested namespaces) |
All other basenames and nested directories, including beam_helpers, stay the
same. Apply the directory and basename rules together; files outside the two
backend directories do not move.
Conflict patterns:
- Cause: another branch edits a moved backend file. Symptom:
rename/modify conflict or a resurrected
backends/newton_femdirectory. Fix: retain thestrain_femdestination and transplant the logical edits; apply the filename rules above to quoted includes and namespace qualifiers. Preserve Newton algorithm attribution and public configuration identifiers. - Cause: another branch adds CMake sources, header globs or source-inspecting tests using the former paths. Symptom: missing-source/configure failures, failed source lookups, or installed headers that omit the backend. Fix: update both CMake source lists and header file-set globs, plus test, tool and documentation paths. Validate installed headers as well as the build.
- Cause: old and new C++ objects are mixed. Symptom: undefined references naming the former namespace. Fix: rebuild dependent libraries and bindings together, then install into an isolated environment. The internal namespace changes mangled symbols; retaining public Python names is not binary compatibility.
PR-specific verification:
git ls-files novaphy | rg '/backends/newton_fem/'
rg -n '#include.*backends/newton_fem|namespace.*newton_fem|newton_fem::' novaphy
# Both commands must have no matches; this historical migration table is intentional.
cmake --build <cpu-build> --target _core
cmake --build <cuda-build> --target _core
python -m pytest python/tests/ -q
ctest --test-dir <cpu-build> --output-on-failure
Check a consumer can include fluid/mpm/implicit/backends/strain_fem/assemble_workspace.h
and name novaphy::mpm::implicit::strain_fem::cuda::launch::AssembleCudaWorkspace
using the installed headers. Confirm Python loads the newly installed _core.
4. Universal verification gates¶
After resolving all git conflicts, run these checks. All must pass
before committing. These gates apply regardless of which PR caused
the conflicts.
Gate 1: Zero source tree injection in sys.path¶
novaphy._core is a compiled C extension that only exists at the
install location. Adding the source python/ directory to sys.path
shadows the installed package.
Expected: zero results, except python/novaphy/viewer/gl_backend.py
under if __name__ == "__main__" (acceptable).
Gate 2: Zero sys.modules replacement¶
Expected: zero results (same exception as Gate 1).
Gate 3: No unresolved git conflicts¶
Expected: no output.
5. Standard merge procedure¶
# 1. Start the merge
git checkout <your-branch>
git fetch origin main
git merge origin/main
# 2. Diagnose (see §2)
git status --short | grep -E '^[UAD]'
# 3. Identify which restructuring PR(s) caused the conflicts (see §3)
# Resolve each conflict using the patterns documented there.
# 4. Universal verification gates (see §4) — DO NOT SKIP
grep -rn 'sys\.path\.insert\|sys\.path\.append' python/
grep -rn 'sys\.modules\[.novaphy.\]' python/
git diff --name-only --diff-filter=U
# 5. PR-specific verification (see the PR entry in §3)
# 6. Commit
git add <resolved-files>
git commit -m "Merge branch 'main' into <your-branch>"
# 7. Verify (if environment supports it)
pytest python/tests/ -v
6. How to extend this document¶
When a new restructuring PR lands in main, add a subsection under §3
using this template:
### 3.X PR #NNN — <short title>
**What changed:**
| Change | Before | After |
|--------|--------|-------|
| <description> | `<old-path>` | `<new-path>` |
**Conflict patterns:**
#### Pattern A: <name>
**Cause:** <why this conflict happens due to this PR's changes>
**Symptom:** <what error or conflict marker the agent will see>
**Fix:** <concrete steps, with copyable commands>
#### Pattern B: ...
**PR-specific verification:**
```bash
<grep command>
# Expected: <what should happen>
Keep each pattern self-contained: an agent encountering it for the first time should be able to diagnose and fix it from this entry alone.
Prevention tips¶
- Alphabetize source lists in
CMakeLists.txt— reduces spurious conflicts when both sides append to the end. - Merge
mainbefore starting a large refactor; land it quickly. - Use
git mvfor renames — merge algorithms handle it better. - Call out moves in the PR description — a sentence like "this PR
moves C++ tests to
novaphy/tests/" saves the next merger significant diagnosis time. - Never use
sys.pathto make the source tree importable. If a demo script needsnovaphy, install the package and import normally.