17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-07 00:22:56 +01:00

fix(physics): articulated couplers stall freight trains (eu07/maszyna#553) (#255)

* fix(physics): articulated couplers stall freight trains (eu07/maszyna#553)

Articulated wagons (T3000e, N-230, N-234) set up with CType=Articulated
barely moved in a freight train and jerked.

- The articulated joint took the spring force up to DmaxC = 0.05 m only,
  225 kN, well short of its FmaxC of 850 kN and of what a loaded section
  in a freight train takes. It overstretched and the sections collided at
  every physics step. Dmax is now sized so the spring carries FmaxC
  (0.19 m stretched, 0.09 m pressed in), never below the old 0.05 m.
- ComputeCollision() scaled the velocities of both vehicles by
  sqrt(1 - beta), so each collision braked the pair as a whole: two
  coupled vehicles in a sustained "collision" lost a third of their speed
  per step. The restitution now applies to the velocities relative to the
  centre of mass only, and the pair keeps its momentum.

Tests: the articulated joint carries FmaxC within its travel; a jerked
articulated set and a jerked screw-coupled pair keep their momentum. On
the code before the fix both pairs stop dead within 3 s.

* style(physics): English-only comment in ComputeCollision()

The comment above the collision velocities started in Polish and went on in English.

* style(physics): SonarQube findings in the #553 fix

std::lerp for the collision velocities (cpp:S6179) and test locals renamed so they no longer shadow the end and coupling enumerators (cpp:S1117).
This commit is contained in:
pawelw
2026-11-05 10:25:26 +01:00
committed by GitHub
parent 09b1c2de10
commit fc22645d7c
2 changed files with 82 additions and 4 deletions

View File

@@ -17,6 +17,7 @@ http://mozilla.org/MPL/2.0/.
#include "utilities/parser.h"
#include "simulation/simulation.h"
#include <cmath>
#include <format>
//---------------------------------------------------------------------------
@@ -51,8 +52,13 @@ double ComputeCollision(double &v1, double &v2, double m1, double m2, double bet
double sum = m1 + m2;
double w1 = (m2 * v2 * 2.0 + v1 * (m1 - m2)) / sum;
double w2 = (m1 * v1 * 2.0 + v2 * (m2 - m1)) / sum;
v1 = w1 * std::sqrt(1.0 - beta); // niejawna zmiana predkosci wskutek zderzenia
v2 = w2 * std::sqrt(1.0 - beta);
// the collision changes the velocities directly: the bounce loses energy in the velocities relative to the
// centre of mass only, the pair keeps its momentum. scaling the velocities themselves braked the vehicles
// down with every step their couplers spent stretched or pressed in all the way (eu07/maszyna#553)
auto const centre{(m1 * v1 + m2 * v2) / sum};
auto const restitution{std::sqrt(1.0 - beta)};
v1 = std::lerp(centre, w1, restitution);
v2 = std::lerp(centre, w2, restitution);
return m1 * (w2 - w1) * (1 - beta);
}
}
@@ -10695,12 +10701,15 @@ void TMoverParameters::LoadFIZ_BuffCoupl(std::string const &line, int const Inde
coupler->beta = 0.55;
*/
thiscoupler->SpringKC = 4500 * 1000;
thiscoupler->DmaxC = 0.05;
thiscoupler->FmaxC = 850 * 1000;
thiscoupler->SpringKB = 9200 * 1000;
thiscoupler->DmaxB = 0.05;
thiscoupler->FmaxB = 320 * 1000;
thiscoupler->beta = 0.55;
// the spring force is taken up to Dmax only, past it the vehicles collide (CollisionDetect()); so the joint
// carries the full FmaxC either way before it gives. with 0.05 m it carried 225 kN, short of what a loaded
// articulated wagon in a freight train takes, which then collided at every step (eu07/maszyna#553)
thiscoupler->DmaxC = std::max(0.05, thiscoupler->FmaxC / thiscoupler->SpringKC);
thiscoupler->DmaxB = std::max(0.05, thiscoupler->FmaxC / thiscoupler->SpringKB);
}
if (Index == 0)

View File

@@ -456,3 +456,72 @@ TEST_CASE("couplers: the coupler force of vehicles facing each other lands on th
CHECK(second->Couplers[end::front].CForce == 0.0);
}
}
// eu07/maszyna#553: articulated wagons (T3000e, N-234 and the like) set up with articulated couplers barely moved in a
// freight train. the joint took the spring force up to 5 cm only, 225 kN, so it overstretched under the train and the
// sections collided at every step; and each collision scaled the velocities of both vehicles down, rather than only
// the velocity of one relative to the other, braking the whole train
namespace {
// the wagon from data/wagon.json, loaded, with articulated couplers at both ends
vehicle articulated_wagon(double Velocity, std::string const &Name) {
return vehicle{definition::load("wagon").set("Param", "M", 60000).set("BuffCoupl", "CType", "Articulated"), Velocity, Name};
}
// couples the facing ends of the two vehicles for good, as the scenery does for an articulated set
void join(vehicle &First, int FirstEnd, vehicle &Second, int SecondEnd) {
REQUIRE(First->Attach(FirstEnd, SecondEnd, &Second.mover(), coupling::coupler, true, false));
REQUIRE(Second->Attach(SecondEnd, FirstEnd, &First.mover(), coupling::coupler, true, false));
}
} // namespace
TEST_CASE("couplers: an articulated joint takes its full tensile force within its travel") {
auto wagon{articulated_wagon(0.0, "wagon")};
auto const &joint{wagon->Couplers[end::front]};
REQUIRE(joint.type() == TCouplerType::Articulated);
CHECK(joint.SpringKC * joint.DmaxC >= doctest::Approx(joint.FmaxC));
CHECK(joint.SpringKB * joint.DmaxB >= doctest::Approx(joint.FmaxC));
// and no less than it had before
CHECK(joint.DmaxC >= 0.05);
CHECK(joint.DmaxB >= 0.05);
}
TEST_CASE("couplers: an articulated set keeps its momentum when one section jerks the other") {
// the front section moving, the rear one barely (so that its brakes are released), joined: the jerk overstretches the joint, the sections collide
// and run on together at half the speed, as the momentum they have between them dictates
auto front_section{articulated_wagon(20.0, "front")};
auto rear_section{articulated_wagon(0.1, "rear")};
join(rear_section, end::front, front_section, end::rear);
test::pair pair{rear_section, end::front, front_section, end::rear, 0.0};
REQUIRE(pair.coupled());
// brakes released, as a locomotive keeps them
front_section.hold_brake_pipe(5.0);
rear_section.hold_brake_pipe(5.0);
pair.run(3.0, test::track{0.15, 0.0});
CHECK(front_section.speed() == doctest::Approx(rear_section.speed()).epsilon(0.1));
// rolling resistance takes a little in the 3 s, the collisions used to take most of it
CHECK(0.5 * (front_section.speed() + rear_section.speed()) > 9.0);
CHECK(0.5 * (front_section.speed() + rear_section.speed()) < 10.05 + 1e-6);
CHECK(pair.coupled());
}
TEST_CASE("couplers: a collision of coupled vehicles keeps the momentum of the pair") {
// the same jerk through screw couplers, which collide when stretched beyond their travel as well
auto front_wagon{vehicle{definition::load("wagon"), 30.0, "front"}};
auto rear_wagon{vehicle{definition::load("wagon"), 0.1, "rear"}};
join(rear_wagon, end::front, front_wagon, end::rear);
test::pair pair{rear_wagon, end::front, front_wagon, end::rear, 0.0};
REQUIRE(pair.coupled());
front_wagon.hold_brake_pipe(5.0);
rear_wagon.hold_brake_pipe(5.0);
pair.run(3.0, test::track{0.15, 0.0});
CHECK(0.5 * (front_wagon.speed() + rear_wagon.speed()) > 13.5);
CHECK(0.5 * (front_wagon.speed() + rear_wagon.speed()) < 15.05 + 1e-6);
}