Salvo Combat Modeling: Battle of Coronel

Introduction

In this blog post (notebook) we calibrate the Heterogeneous Salvo Combat Model (HSCM), [MJ1, AAp1, AAp2], to the First World War Battle of Coronel, [Wk1]. Our goal is to exemplify the usage of the functionalities of the package “Math::SalvoCombatModeling”, [AAp1]. We closely follow the Section B of Chapter III of [MJ1]. The calibration data used in [MJ1] is taken from [TB1].

Remark: The implementation of the Raku package “Math::SalvoCombatModeling”, [AAp1], closely follows the implementation of the Wolfram Language (WL) paclet “SalvoCombatModeling”, [AAp2]. Since WL has (i) symbolic builtin computations and (ii) a mature notebook system the salvo models computation, representation, and study with WL is much more convenient.


Setup

Here we load the package:

use Math::SalvoCombatModeling;
use Graph;

The battle

The Battle of Coronel is a First World War naval engagement between three British ships {Good Hope, Monmouth, and Glasgow) and four German ships (Scharnhorst, Gneisenau, Leipzig, and Dresden). The battle happened on 1 November 1914, off the coast of central Chile near the city of Coronel.

The Scharnhorst and Gneisenau are the first ships to open fire at Good Hope and Monmouth; the three British ships soon afterwards return fire. Dresden and Leipzig open fire on Glasgow, driving her out of the engagement. At the end of the battle, both Good Hope and Monmouth are sunk, while Glasgow, Scharnhorst, and Gneisenau were damaged.

ShipDuration of fire
Good Hope0
Monmouth0
Glasgow15
Scharnhorst28
Gneisenau28
Leipzig2
Dresden2

The following graph shows which ship shot at which ships and total fire duration (in minutes):

#% html
my @edges =
{ from =>'Scharnhorst', to =>'Good Hope', weight => 28 },
{ from =>'Scharnhorst', to =>'Monmouth', weight => 28 },
{ from =>'Gneisenau', to =>'Good Hope', weight => 28 },
{ from =>'Gneisenau', to =>'Monmouth', weight => 28 },
{ from =>'Leipzig', to =>'Glasgow', weight => 2 },
{ from =>'Glasgow', to =>'Scharnhorst', weight => 2 },
{ from =>'Glasgow', to =>'Gneisenau', weight => 15 },
{ from =>'Glasgow', to =>'Leipzig', weight => 15 },
{ from =>'Glasgow', to =>'Dresden', weight => 15 },
{ from =>'Dresden', to =>'Glasgow', weight => 15 };
my $g = Graph.new(@edges):directed;
$g.dot(
engine => 'neato',
vertex-shape => 'ellipse',
vertex-width => 0.65,
:5size,
:8vertex-font-size,
:weights,
:6edge-font-size,
edge-thickness => 0.8,
arrow-size => 0.6
):svg;

Salvo combat modeling definitions

Before going with building the model here is table that provides definitions of the fundamental notions of salvo combat modeling:

#% html
salvo-notion-definitions('English')
==> to-html(field-names => <notion definition>, align => 'left')
notiondefinition
ForceA group of naval ships that operate and fight together.
UnitA unit is an individual ship in a force.
SalvoA salvo is the number of shots fired as a unit of force in a discrete period of time.
Combat PotentialCombat Potential is a force’s total stored offensive capability of an element or force measured in number of total shots available.
Combat PowerAlso called Striking Power, is the maximum offensive capability of an element or force per salvo, measured in the number of hitting shots that would be achieved in the absence of degrading factors.
Scouting EffectivenessScouting Effectiveness is a dimensionless degradation factor applied to a force’s combat power as a result of imperfect information. It is a number between zero and one that describes the difference between the shots delivered based on perfect knowledge of enemy composition and position and shots based on existing information [Ref. 7].
Training EffectivenessTraining effectiveness is a fraction that indicates the degradation in combat power due the lack of training, motivation, or readiness.
Distraction FactorAlso called chaff effectiveness or seduction, is a multiplier that describes the effectiveness of an offensive weapon in the presence of distraction or other soft kill. This multiplier is a fraction, where one indicates no susceptibility/complete effectiveness and zero indicates complete susceptibility/no effectiveness.
Offensive EffectivenessOffensive effectiveness is a composite term made of the product of scouting effectiveness, training effectiveness, distraction, or any other factor which represents the probability of a single salvo hitting its target. Offensive effectiveness transforms a unit’s combat potential parameter into combat power.
Defensive PotentialDefensive potential is a force’s total defensive capability measured in units of enemy hits eliminated independent of weapon system or operator accuracy or any other multiplicative factor.
Defensive PowerDefensive power is the number of missiles in an enemy salvo that a defending element or force can eliminate.
Defender AlertnessDefender alertness is the extent to which a defender fails to take proper defensive actions against enemy fire. This may be the result of any inattentiveness due to improper emission control procedures, readiness, or other similar factors. This multiplier is a fraction, where one indicates complete alertness and zero indicates no alertness.
Defensive EffectivenessDefensive effectiveness is a composite term made of the product of training effectiveness and defender alertness. This term also applies to any value that represents the overall degradation of a force’s defensive power.
Staying PowerStaying power is the number of hits that a unit or force can absorb before being placed out of action.

Model

The British ships are in Good HopeMonmouth, and Glasgow. They correspond to the indices 1, ,2, and 3 respectively.

["Good Hope", "Monmouth", "Glasgow"] Z=> 1..3
# (Good Hope => 1 Monmouth => 2 Glasgow => 3)

The German ships are ScharnhorstGneisenauLeipzig, and Dresden:

["Scharnhorst", "Gneisenau", "Leipzig", "Dresden"] Z=> 1..4
# (Scharnhorst => 1 Gneisenau => 2 Leipzig => 3 Dresden => 4)

Remark: The Battle of Coronel is modeled with a “typical” salvo model — the ships use “continuous fire.” Hence, there are no interceptors and or, in model terms, defense terms or matrices.

Here is the model (for 3 British ships and 4 German ships):

sink my $m = heterogeneous-salvo-model(['B', 3], ['G', 4]):latex;

Remove the defense matrices (i.e. make them zero):

sink $m<B><defense-matrix> = ((0 xx $m<B><defense-matrix>.head.elems).Array xx $m<B><defense-matrix>.elems).Array;
sink $m<G><defense-matrix> = ((0 xx $m<G><defense-matrix>.head.elems).Array xx $m<G><defense-matrix>.elems).Array;

Converting the obtained model data structure to LaTeX we get:

Concrete parameter values

Setting the parameter values as in [MJ1] defining the sub param (to be passed heterogeneous-salvo-model):

multi sub param(Str:D $name, Str:D $a where * eq 'B', Str:D $b where * eq 'G', UInt:D $i, UInt:D $j) { 0 }
multi sub param(Str:D $name, Str:D $a where * eq 'G', Str:D $b where * eq 'B', UInt:D $i, UInt:D $j) {
given $name {
when 'beta' {
given ($i, $j) {
when (1, 1) { 2.16 }
when (1, 2) { 2.16 }
when (1, 3) { 2.16 }
when (2, 1) { 2.16 }
when (2, 2) { 2.16 }
when (2, 3) { 2.16 }
when (3, 1) { 2.165 }
when (3, 2) { 2.165 }
when (3, 3) { 2.165 }
when (4, 1) { 2.165 }
when (4, 2) { 2.165 }
when (4, 3) { 2.165 }
}
}
when 'curlyepsilon' {
given ($i, $j) {
when (1, 1) { 0.028 }
when (1, 2) { 0.028 }
when (1, 3) { 0.028 }
when (2, 1) { 0.028 }
when (2, 2) { 0.028 }
when (2, 3) { 0.028 }
when (3, 1) { 0.012 }
when (3, 2) { 0.012 }
when (3, 3) { 0.012 }
when (4, 1) { 0.012 }
when (4, 2) { 0.012 }
when (4, 3) { 0.012 }
}
}
when 'capitalpsi' {
given ($i, $j) {
when (1, 1) { 0.5 }
when (1, 2) { 0.5 }
when (2, 1) { 0.5 }
when (2, 2) { 0.5 }
when (3, 1) { 0 }
when (3, 2) { 0 }
when (4, 1) { 0 }
when (4, 2) { 0 }
when (1, 3) { 0 }
when (2, 3) { 0 }
when (3, 3) { 1 }
when (4, 3) { 1 }
}
}
}
}
multi sub param(Str:D $name, Str:D $a where * eq 'G', UInt:D $i) { 1 }
multi sub param(Str:D $name, Str:D $a where * eq 'B', UInt:D $i) {
given $name {
when 'zeta' {
given $i {
when 1 { 1.605 }
when 2 { 1.605 }
when 3 { 1.23 }
}
}
}
}
multi sub param(Str:D $name where $name eq 'units', Str:D $a where * eq 'B', UInt:D $i) { $i }
multi sub param(Str:D $name where $name eq 'units', Str:D $a where * eq 'G', UInt:D $i) { $i }
# &param

Damage calculations

my $m = heterogeneous-salvo-model(['B', 3], ['G', 4], :offensive-effectiveness-terms, :&param)
# {B => {defense-matrix => [[0 0 0] [0 0 0] [0 0 0]], offense-matrix => [[0.018841 0.018841 0 0] [0.018841 0.018841 0 0] [0 0 0.021122 0.021122]], units => [1 2 3]}, G => {defense-matrix => [[0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0]], offense-matrix => [[0 0 0] [0 0 0] [0 0 0] [0 0 0]], units => [1 2 3 4]}}
$m<B><offense-matrix>
# [[0.018841 0.018841 0 0] [0.018841 0.018841 0 0] [0 0 0.021122 0.021122]]
my $ΔB = $m<B><offense-matrix>».sum
# (0.037682 0.037682 0.042244)

How many salvos to achieve total damage of Good Hope and Monmouth:

1 / $ΔB.head
# 26.537698

That is close to the 28 min of fire by Scharnhorst and Gneisenau at Good Hope and Monmouth.

Total damage of on Glasgow — Leipzig and Dresden fire for 2 min at Glasgow:

$ΔB.tail * 2
# 0.084488

References

Articles, theses

[MJ1] Michael D. Johns, Steven E. Pilnick, Wayne P. Hughes, “Heterogeneous Salvo Model for the Navy After Next”, (2000), Defense Technical Information Center.

[TB1] Thomas R. Beall, “The Development of a Naval Battle Model and Its Validation Using Historical Data”, (1990), Defense Technical Information Center.

[Wk1] Wikipedia entry, Salvo combat model.

Packages, paclets

[AAp1] Anton Antonov, Math::SalvoCombatModeling, Raku package, (2026), GitHib/antononcube.

[AAp2] Anton Antonov, SalvoCombatModeling, Wolfram Language paclet, (2024), Wolfram Language Paclet Repository.