TerrainGen coverage


terraingen/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
202 / 220
91.8%
Branches:
136 / 158
86.1%
Functions:
43 / 46
93.5%

python/src/distribution.cpp
Line Branch Exec Source
1 #include "bindings.h"
2 #include "common.h"
3
4 #include "terraingen/distribution/equilibrium.h"
5 #include "terraingen/distribution/golombek_rapp.h"
6 #include "terraingen/distribution/ivanov.h"
7 #include "terraingen/distribution/log_polynomial.h"
8 #include "terraingen/distribution/minimum.h"
9 #include "terraingen/distribution/moore.h"
10 #include "terraingen/distribution/neukum.h"
11 #include "terraingen/distribution/piecewise_power_law.h"
12 #include "terraingen/distribution/piecewise_uniform.h"
13 #include "terraingen/distribution/power_law.h"
14 #include "terraingen/distribution/production_surface.h"
15 #include "terraingen/distribution/size_frequency.h"
16 #include "terraingen/distribution/sp8023.h"
17 #include "terraingen/distribution/spliced.h"
18 #include "terraingen/distribution/summed.h"
19 #include "terraingen/distribution/synthterrain.h"
20 #include "terraingen/distribution/tabulated.h"
21 #include "terraingen/distribution/viper.h"
22
23 #include <nanobind/trampoline.h>
24
25 #include <limits>
26 #include <tuple>
27 #include <utility>
28 #include <vector>
29
30 namespace terraingen::python {
31
32 namespace {
33
34 constexpr double kInfinity = std::numeric_limits<double>::infinity();
35
36 using Sfd = SizeFrequencyDistribution;
37 using SfdPointer = std::shared_ptr<Sfd>;
38 using StartAndSfd = std::pair<double, SfdPointer>;
39
40 /// @brief A distribution returned by value, held as Python holds distributions.
41 184 template <typename T> SfdPointer Shared(T value) {
42
4/4
std::shared_ptr<terraingen::SizeFrequencyDistribution> terraingen::python::(anonymous namespace)::Shared<terraingen::LogPolynomialSfd>(terraingen::LogPolynomialSfd):
✓ Branch 2 taken 7 times.
std::shared_ptr<terraingen::SizeFrequencyDistribution> terraingen::python::(anonymous namespace)::Shared<terraingen::PiecewisePowerLawSfd>(terraingen::PiecewisePowerLawSfd):
✓ Branch 2 taken 6 times.
std::shared_ptr<terraingen::SizeFrequencyDistribution> terraingen::python::(anonymous namespace)::Shared<terraingen::PowerLawSfd>(terraingen::PowerLawSfd):
✓ Branch 2 taken 86 times.
std::shared_ptr<terraingen::SizeFrequencyDistribution> terraingen::python::(anonymous namespace)::Shared<terraingen::SplicedSfd>(terraingen::SplicedSfd):
✓ Branch 2 taken 85 times.
184 return std::make_shared<T>(std::move(value));
43 }
44
45 /// @brief A size-frequency distribution written in Python: `cumulative_at(diameter)` is required;
46 /// `incremental_at` and `invert_cumulative` refine the numerical defaults.
47 class PySizeFrequencyDistribution : public Sfd {
48 public:
49 NB_TRAMPOLINE(Sfd);
50
51 10 PySizeFrequencyDistribution(SfdKind kind, double minDiameter, double maxDiameter)
52 10 : Sfd(kind, DiameterRange{minDiameter, maxDiameter}) {}
53
54 /// @brief The hooks of any distribution, for the base class's Python methods of the same
55 /// names.
56 ✗ static double CumulativeAtOf(const Sfd& sfd, double diameter) {
57 ✗ return CumulativeOf(sfd, diameter);
58 }
59 ✗ static double IncrementalAtOf(const Sfd& sfd, double diameter) {
60 ✗ return IncrementalOf(sfd, diameter);
61 }
62 ✗ static double InvertCumulativeAtOf(const Sfd& sfd, double target, double dMin, double dMax) {
63 ✗ return InvertCumulativeOf(sfd, target, dMin, dMax);
64 }
65
66 protected:
67 18577 double CumulativeAt(double diameter) const override {
68
2/2
✓ Branch 2 taken 18576 times.
✓ Branch 5 taken 18576 times.
111457 NB_OVERRIDE_PURE_NAME("cumulative_at", CumulativeAt, diameter);
69 }
70
71 1 double IncrementalAt(double diameter) const override {
72
2/5
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✓ Branch 11 taken 1 time.
2 NB_OVERRIDE_NAME("incremental_at", IncrementalAt, diameter);
73 }
74
75 2847 double InvertCumulative(double target, double dMin, double dMax) const override {
76
2/5
✗ Branch 1 not taken.
✓ Branch 2 taken 2847 times.
✗ Branch 4 not taken.
✗ Branch 7 not taken.
✓ Branch 11 taken 2847 times.
5694 NB_OVERRIDE_NAME("invert_cumulative", InvertCumulative, target, dMin, dMax);
77 }
78 };
79
80 /// @brief (start, distribution) pairs of `parts` as Python holds them.
81 2 template <typename Part> std::vector<StartAndSfd> Pairs(const std::vector<Part>& parts) {
82 2 std::vector<StartAndSfd> pairs;
83
4/4
std::vector<std::pair<double, std::shared_ptr<terraingen::SizeFrequencyDistribution> >, std::allocator<std::pair<double, std::shared_ptr<terraingen::SizeFrequencyDistribution> > > > terraingen::python::(anonymous namespace)::Pairs<terraingen::SfdPiece>(std::vector<terraingen::SfdPiece, std::allocator<terraingen::SfdPiece> > const&):
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
std::vector<std::pair<double, std::shared_ptr<terraingen::SizeFrequencyDistribution> >, std::allocator<std::pair<double, std::shared_ptr<terraingen::SizeFrequencyDistribution> > > > terraingen::python::(anonymous namespace)::Pairs<terraingen::WeightedSfd>(std::vector<terraingen::WeightedSfd, std::allocator<terraingen::WeightedSfd> > const&):
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 1 time.
4 for (const Part& part : parts) {
84 if constexpr (requires { part.weight; }) {
85
1/1
✓ Branch 2 taken 1 time.
1 pairs.emplace_back(part.weight, Mutable(part.distribution));
86 } else {
87
1/1
✓ Branch 2 taken 1 time.
1 pairs.emplace_back(part.start, Mutable(part.distribution));
88 }
89 }
90 2 return pairs;
91 }
92
93 44 void BindBase(nb::module_& m) {
94 88 nb::enum_<SfdKind>(m, "SfdKind", "What a size-frequency distribution counts.")
95 44 .value("DENSITY", SfdKind::Density, "Craters or boulders per square metre.")
96 .value("PRODUCTION_RATE", SfdKind::ProductionRate, "Per square metre per Gyr.");
97 using Py = PySizeFrequencyDistribution;
98 ✗ nb::class_<Sfd, Py>(
99 m, "SizeFrequencyDistribution",
100 "A cumulative size-frequency distribution N(>= D) per square metre (or per square metre "
101 "per Gyr for a production rate), diameters in metres, valid between `min_diameter` and "
102 "`max_diameter`. Subclass it and implement `cumulative_at(diameter)` (and optionally "
103 "`incremental_at(diameter)`, -dN/dD, and `invert_cumulative(target, d_min, d_max)`) for "
104 "a distribution of your own.")
105
1/1
✓ Branch 1 taken 44 times.
88 .def(nb::init<SfdKind, double, double>(), "kind"_a = SfdKind::Density,
106
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
132 "min_diameter"_a = 0.0, "max_diameter"_a = kInfinity)
107
1/1
✓ Branch 1 taken 44 times.
44 .def("cumulative_at", &Py::CumulativeAtOf, "diameter"_a,
108 "N(>= diameter) without range checks; override it in a subclass.")
109
1/1
✓ Branch 1 taken 44 times.
44 .def("incremental_at", &Py::IncrementalAtOf, "diameter"_a,
110 "-dN/dD without range checks; numerical unless overridden.")
111
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
44 .def("invert_cumulative", &Py::InvertCumulativeAtOf, "target"_a, "min_diameter"_a,
112
1/1
✓ Branch 1 taken 44 times.
44 "max_diameter"_a,
113 "The diameter between the two where N(>= D) is `target`; numerical unless "
114 "overridden.")
115 88 .def_prop_ro("kind", &Sfd::Kind)
116 44 .def_prop_ro(
117 "valid_range",
118 31 [](const Sfd& sfd) { return std::pair{sfd.ValidRange().min, sfd.ValidRange().max}; })
119 44 .def_prop_rw("allows_extrapolation", &Sfd::AllowsExtrapolation, &Sfd::SetAllowExtrapolation)
120
1/1
✓ Branch 1 taken 44 times.
44 .def("cumulative", &Sfd::Cumulative, "diameter"_a, "N(>= diameter).")
121
1/1
✓ Branch 1 taken 44 times.
44 .def("incremental", &Sfd::Incremental, "diameter"_a, "-dN/dD at `diameter`.")
122
3/3
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
44 .def("expected_count", &Sfd::ExpectedCount, "area"_a, "min_diameter"_a, "max_diameter"_a,
123 "Expected number over `area` square metres between the diameters.")
124
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
44 .def("sample_diameter", &Sfd::SampleDiameter, "random"_a, "min_diameter"_a,
125
1/1
✓ Branch 1 taken 44 times.
88 "max_diameter"_a, "A diameter drawn from the distribution between the two.");
126 44 }
127
128 44 void BindPowerLaws(nb::module_& m) {
129 44 nb::class_<PowerLawSfd, Sfd>(m, "PowerLawSfd",
130 "N(>= D) = coefficient * D^-exponent, D in metres.")
131 44 .def(
132 "__init__",
133 64 [](PowerLawSfd* self, double coefficient, double exponent, SfdKind kind,
134 double minDiameter, double maxDiameter) {
135
1/3
✓ Branch 2 taken 20 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
20 new (self) PowerLawSfd(coefficient, exponent, kind, {minDiameter, maxDiameter});
136 20 },
137
4/4
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
✓ Branch 10 taken 44 times.
176 "coefficient"_a, "exponent"_a, "kind"_a = SfdKind::Density, "min_diameter"_a = 0.0,
138
1/1
✓ Branch 1 taken 44 times.
88 "max_diameter"_a = kInfinity)
139 44 .def_prop_ro("coefficient", &PowerLawSfd::Coefficient)
140 88 .def_prop_ro("exponent", &PowerLawSfd::Exponent);
141 88 nb::class_<PowerLawSegment>(m, "PowerLawSegment",
142 "One piece of a piecewise power law, from `start` metres.")
143 44 .def_ro("start", &PowerLawSegment::start)
144 44 .def_ro("coefficient", &PowerLawSegment::coefficient)
145 .def_ro("exponent", &PowerLawSegment::exponent);
146
1/1
✓ Branch 1 taken 44 times.
44 m.def("power_law_coefficient_from_kilometres", &PowerLawCoefficientFromKilometres,
147
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
88 "coefficient"_a, "exponent"_a,
148 "The coefficient per square metre, D in metres, of a power law given per square "
149 "kilometre with D in kilometres.");
150 ✗ nb::class_<PiecewisePowerLawSfd, Sfd>(
151 m, "PiecewisePowerLawSfd",
152 "Power laws over consecutive diameter ranges: `segments` are (start diameter, "
153 "coefficient, exponent), each holding from its start to the next.")
154 44 .def(
155 "__init__",
156 46 [](PiecewisePowerLawSfd* self,
157 const std::vector<std::tuple<double, double, double>>& segments, SfdKind kind,
158 double minDiameter, double maxDiameter, bool allowDiscontinuities) {
159 2 std::vector<PowerLawSegment> pieces;
160
2/2
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 2 times.
6 for (const auto& [start, coefficient, exponent] : segments) {
161
1/1
✓ Branch 1 taken 4 times.
4 pieces.push_back({start, coefficient, exponent});
162 }
163
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ new (self) PiecewisePowerLawSfd(std::move(pieces), kind, {minDiameter, maxDiameter},
164
1/1
✓ Branch 4 taken 2 times.
2 allowDiscontinuities);
165 2 },
166
3/3
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
176 "segments"_a, "kind"_a = SfdKind::Density, "min_diameter"_a = 0.0,
167
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
132 "max_diameter"_a = kInfinity, "allow_discontinuities"_a = false)
168 88 .def_prop_ro("segments", &PiecewisePowerLawSfd::Segments);
169 44 }
170
171 44 void BindTables(nb::module_& m) {
172 44 nb::class_<TabulatedSfd, Sfd>(
173 m, "TabulatedSfd",
174 "Cumulative counts at given diameters, interpolated log-log. `diameter_unit` and "
175 "`area_unit` convert the table to metres and square metres (1000 and 1e6 for km and "
176 "km^2).")
177 132 .def(nb::init<std::vector<double>, std::vector<double>, SfdKind, double, double>(),
178
4/4
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
✓ Branch 10 taken 44 times.
176 "diameters"_a, "cumulative"_a, "kind"_a = SfdKind::Density, "diameter_unit"_a = 1.0,
179
1/1
✓ Branch 1 taken 44 times.
88 "area_unit"_a = 1.0)
180 44 .def_prop_ro("diameters", &TabulatedSfd::Diameters, "Diameters in metres.")
181 88 .def_prop_ro("cumulative_values", &TabulatedSfd::CumulativeValues,
182 "N(>= D) per square metre at each diameter.");
183 88 nb::class_<UniformBand>(m, "UniformBand",
184 "A band of `density` features per square metre with diameters spread "
185 "uniformly from `min` to `max` metres.")
186 44 .def_ro("min", &UniformBand::min)
187 44 .def_ro("max", &UniformBand::max)
188 .def_ro("density", &UniformBand::density);
189 44 nb::class_<PiecewiseUniformSfd, Sfd>(
190 m, "PiecewiseUniformSfd",
191 "Counts in diameter bands, each with diameters spread uniformly: `bands` are (min, max, "
192 "density) with diameters in metres and densities per square metre. A positive `total` "
193 "takes the densities as relative weights and shares `total` per square metre among them.")
194 44 .def(
195 "__init__",
196 46 [](PiecewiseUniformSfd* self,
197 const std::vector<std::tuple<double, double, double>>& bands, SfdKind kind,
198 double total) {
199 2 std::vector<UniformBand> pieces;
200
2/2
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 2 times.
6 for (const auto& [min, max, density] : bands) {
201
1/1
✓ Branch 1 taken 4 times.
4 pieces.push_back({min, max, density});
202 }
203
1/3
✓ Branch 4 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 new (self) PiecewiseUniformSfd(std::move(pieces), kind, total);
204 2 },
205
3/3
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
176 "bands"_a, "kind"_a = SfdKind::Density, "total"_a = 0.0)
206 88 .def_prop_ro("bands", &PiecewiseUniformSfd::Bands);
207 ✗ nb::class_<LogPolynomialSfd, Sfd>(
208 m, "LogPolynomialSfd",
209 "log10 N(>= D) as a polynomial in log10 D, with `coefficients` from the constant term "
210 "up, as for the Neukum production function.")
211 44 .def(
212 "__init__",
213 46 [](LogPolynomialSfd* self, std::vector<double> coefficients, double minDiameter,
214 double maxDiameter, SfdKind kind, double diameterUnit, double areaUnit) {
215
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ new (self) LogPolynomialSfd(std::move(coefficients), {minDiameter, maxDiameter},
216
1/1
✓ Branch 4 taken 2 times.
2 kind, diameterUnit, areaUnit);
217 2 },
218
4/4
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
✓ Branch 10 taken 44 times.
88 "coefficients"_a, "min_diameter"_a, "max_diameter"_a, "kind"_a = SfdKind::Density,
219
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
132 "diameter_unit"_a = 1.0, "area_unit"_a = 1.0)
220 88 .def_prop_ro("coefficients", &LogPolynomialSfd::Coefficients);
221 44 }
222
223 44 void BindSumAndMinimum(nb::module_& m) {
224 44 nb::class_<SummedSfd, Sfd>(
225 m, "SummedSfd", "A weighted sum of distributions: `terms` are (weight, distribution).")
226 44 .def(
227 "__init__",
228 46 [](SummedSfd* self, const std::vector<StartAndSfd>& terms) {
229 2 std::vector<WeightedSfd> weighted;
230
2/2
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 2 times.
5 for (const auto& [weight, distribution] : terms) {
231
1/1
✓ Branch 1 taken 3 times.
3 weighted.push_back({weight, distribution});
232 }
233
1/3
✓ Branch 4 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 new (self) SummedSfd(std::move(weighted));
234 5 },
235
1/1
✓ Branch 1 taken 44 times.
44 "terms"_a)
236 45 .def_prop_ro("terms", [](const SummedSfd& sfd) { return Pairs(sfd.Terms()); });
237 ✗ nb::class_<MinimumSfd, Sfd>(
238 m, "MinimumSfd",
239 "The smallest of several distributions at each diameter, such as production capped by "
240 "equilibrium.")
241 44 .def(
242 "__init__",
243 122 [](MinimumSfd* self, const std::vector<SfdPointer>& distributions) {
244
2/4
✓ Branch 4 taken 78 times.
✓ Branch 7 taken 78 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
156 new (self) MinimumSfd({distributions.begin(), distributions.end()});
245 78 },
246
1/1
✓ Branch 1 taken 44 times.
44 "distributions"_a)
247 44 .def_prop_ro("distributions", [](const MinimumSfd& sfd) {
248 1 std::vector<SfdPointer> parts;
249
2/2
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 time.
3 for (const auto& part : sfd.Distributions()) {
250
1/1
✓ Branch 2 taken 2 times.
2 parts.push_back(Mutable(part));
251 }
252 1 return parts;
253 ✗ });
254 44 }
255
256 44 void BindSpliced(nb::module_& m) {
257 ✗ nb::class_<SplicedSfd, Sfd>(
258 m, "SplicedSfd",
259 "Distributions joined end to end: `pieces` are (start diameter, distribution), each "
260 "holding from its start to the next.")
261 44 .def(
262 "__init__",
263 46 [](SplicedSfd* self, const std::vector<StartAndSfd>& pieces, double minDiameter,
264 double maxDiameter) {
265 2 std::vector<SfdPiece> spliced;
266
2/2
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 2 times.
5 for (const auto& [start, distribution] : pieces) {
267
1/1
✓ Branch 1 taken 3 times.
3 spliced.push_back({start, distribution});
268 }
269
1/3
✓ Branch 4 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2 new (self) SplicedSfd(std::move(spliced), {minDiameter, maxDiameter});
270 5 },
271
3/3
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
44 "pieces"_a, "min_diameter"_a, "max_diameter"_a)
272 45 .def_prop_ro("pieces", [](const SplicedSfd& sfd) { return Pairs(sfd.Pieces()); });
273 44 }
274
275 44 void BindGolombekRapp(nb::module_& m) {
276 ✗ nb::class_<GolombekRappSfd, Sfd>(
277 m, "GolombekRappSfd",
278 "Golombek and Rapp's (1997) boulders: the fractional area covered by boulders at least D "
279 "across is coverage * exp(-q D), q = qa + qb / coverage.")
280 44 .def(
281 "__init__",
282 ✗ [](GolombekRappSfd* self, double coverage, double qa, double qb, double minDiameter,
283 double maxDiameter) {
284
1/3
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1 new (self) GolombekRappSfd(coverage, qa, qb, {minDiameter, maxDiameter});
285 1 },
286
4/4
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
✓ Branch 7 taken 44 times.
✓ Branch 10 taken 44 times.
220 "coverage"_a, "qa"_a = 1.79, "qb"_a = 0.152, "min_diameter"_a = 0.0,
287
1/1
✓ Branch 1 taken 44 times.
88 "max_diameter"_a = kInfinity)
288 44 .def_prop_ro("coverage", &GolombekRappSfd::Coverage)
289 44 .def_prop_ro("q", &GolombekRappSfd::Q)
290
1/1
✓ Branch 1 taken 44 times.
88 .def("fractional_area", &GolombekRappSfd::FractionalArea, "diameter"_a);
291 44 }
292
293 44 void BindProductionSurface(nb::module_& m) {
294 ✗ nb::class_<ProductionSurface, Sfd>(
295 m, "ProductionSurface",
296 "The craters a production function leaves on a surface `age_gyr` old: its shape scaled "
297 "by `chronology`, such as `neukum_lunar_chronology`.")
298
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
44 .def(nb::init<SfdPointer, double, const Chronology&>(), "production"_a, "age_gyr"_a,
299
1/1
✓ Branch 1 taken 44 times.
88 "chronology"_a)
300 44 .def_prop_ro("age_gyr", &ProductionSurface::AgeGyr)
301 88 .def_prop_ro("production", &ProductionSurface::Production,
302 nb::rv_policy::reference_internal,
303 "The production function scaled to the surface.");
304 44 }
305
306 44 void BindLunarCraterDistributions(nb::module_& m) {
307
1/1
✓ Branch 1 taken 44 times.
44 m.def(
308
2/2
✓ Branch 2 taken 83 times.
✓ Branch 5 taken 83 times.
83 "trask_equilibrium", [] { return Shared(TraskEquilibrium()); },
309 "Trask's (1966) lunar crater equilibrium, N(>= D) = 10^-1.1 D^-2 per square metre.");
310
1/1
✓ Branch 1 taken 44 times.
44 m.def(
311
2/2
✓ Branch 3 taken 3 times.
✓ Branch 6 taken 3 times.
47 "hartmann_equilibrium", [] { return Shared(HartmannEquilibrium()); },
312 "Hartmann's lunar crater equilibrium.");
313 88 nb::enum_<MooreTerrain>(m, "MooreTerrain", "Moore's (1971) lunar terrain classes.")
314 44 .value("ROUGH_MARE", MooreTerrain::RoughMare)
315 44 .value("SMOOTH_MARE", MooreTerrain::SmoothMare)
316 44 .value("UPLANDS", MooreTerrain::Uplands)
317 .value("HEAVILY_CRATERED", MooreTerrain::HeavilyCratered);
318
1/1
✓ Branch 1 taken 44 times.
44 m.def(
319 "moore_distribution",
320
3/3
✓ Branch 1 taken 44 times.
✓ Branch 5 taken 5 times.
✓ Branch 8 taken 5 times.
49 [](MooreTerrain terrain) { return Shared(MooreDistribution(terrain)); }, "terrain"_a,
321 "Moore's (1971) crater distribution for a lunar terrain class.");
322
1/1
✓ Branch 1 taken 44 times.
44 m.def(
323
2/2
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
1 "viper_specification", [] { return Shared(ViperSpecification()); },
324 "The VIPER mission's crater specification.");
325
1/1
✓ Branch 1 taken 44 times.
44 m.def(
326
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
2 "neukum_production_function", [] { return Shared(NeukumProductionFunction()); },
327 "Neukum's lunar production function, per square metre per Gyr at today's rate.");
328
1/1
✓ Branch 1 taken 44 times.
44 m.def(
329 "extended_neukum_production_function",
330
2/2
✓ Branch 2 taken 85 times.
✓ Branch 5 taken 85 times.
85 [] { return Shared(ExtendedNeukumProductionFunction()); },
331 "The Neukum production function extended to small craters, as DSNE uses it.");
332
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
44 m.def("neukum_lunar_chronology", &NeukumLunarChronology, "age_gyr"_a,
333 "Neukum's lunar chronology: craters at least 1 km across per square metre on a "
334 "surface `age_gyr` old.");
335
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
44 m.def("neukum_lunar_chronology_rate", &NeukumLunarChronologyRate, "age_gyr"_a,
336 "dN1/dT of Neukum's lunar chronology, per square metre per Gyr.");
337 44 }
338
339 44 void BindMartianCraterDistributions(nb::module_& m) {
340
1/1
✓ Branch 1 taken 44 times.
44 m.def(
341
2/2
✓ Branch 2 taken 5 times.
✓ Branch 5 taken 5 times.
5 "ivanov_mars_production_function", [] { return Shared(IvanovMarsProductionFunction()); },
342 "Ivanov's (2001) Martian production function.");
343
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
44 m.def("ivanov_mars_chronology", &IvanovMarsChronology, "age_gyr"_a,
344 "Ivanov's (2001) Martian chronology, N(>= 1 km) per square metre.");
345
2/2
✓ Branch 1 taken 44 times.
✓ Branch 4 taken 44 times.
44 m.def("ivanov_mars_chronology_rate", &IvanovMarsChronologyRate, "age_gyr"_a,
346 "dN1/dT of Ivanov's Martian chronology, per square metre per Gyr.");
347 44 }
348
349 44 void BindBoulderDistributions(nb::module_& m) {
350 88 nb::enum_<SpecificationCurve>(m, "SpecificationCurve", "Which SP-8023 boulder curve.")
351 44 .value("MINIMUM", SpecificationCurve::Minimum)
352 44 .value("NOMINAL", SpecificationCurve::Nominal)
353 .value("MAXIMUM", SpecificationCurve::Maximum);
354
1/1
✓ Branch 1 taken 44 times.
44 m.def(
355 "sp8023_intercrater_blocks",
356
1/1
✓ Branch 1 taken 4 times.
4 [](SpecificationCurve curve) { return SfdPointer(Sp8023IntercraterBlocks(curve)); },
357
1/1
✓ Branch 1 taken 44 times.
44 "curve"_a, "NASA SP-8023 blocks between craters.");
358
1/1
✓ Branch 1 taken 44 times.
44 m.def(
359 "sp8023_crater_blocks",
360
2/2
✓ Branch 1 taken 44 times.
✓ Branch 6 taken 4 times.
92 [](SpecificationCurve curve) { return SfdPointer(Sp8023CraterBlocks(curve)); }, "curve"_a,
361 "NASA SP-8023 blocks near craters.");
362 88 nb::enum_<SynthterrainBoulders>(m, "SynthterrainBoulders",
363 "synthterrain's boulder distributions.")
364 44 .value("INTER_CRATER", SynthterrainBoulders::InterCrater)
365 44 .value("HAWORTH", SynthterrainBoulders::Haworth)
366 .value("VIPER_SPECIFICATION", SynthterrainBoulders::ViperSpecification);
367
1/1
✓ Branch 1 taken 44 times.
44 m.def(
368 "synthterrain_boulder_distribution",
369 ✗ [](SynthterrainBoulders boulders) {
370
1/1
✓ Branch 1 taken 7 times.
7 return SfdPointer(SynthterrainBoulderDistribution(boulders));
371 },
372
1/1
✓ Branch 1 taken 44 times.
44 "boulders"_a, "One of synthterrain's power-law boulder distributions.");
373 44 }
374
375 } // namespace
376
377 44 void BindDistribution(nb::module_& m) {
378 44 BindBase(m);
379 44 BindPowerLaws(m);
380 44 BindTables(m);
381 44 BindSumAndMinimum(m);
382 44 BindSpliced(m);
383 44 BindGolombekRapp(m);
384 44 BindProductionSurface(m);
385 44 BindLunarCraterDistributions(m);
386 44 BindMartianCraterDistributions(m);
387 44 BindBoulderDistributions(m);
388 44 }
389
390 } // namespace terraingen::python
391