distribution/size_frequency.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief The size-frequency distribution base class, its kinds and diameter ranges. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/random/stream.h" | ||
| 6 | |||
| 7 | #include <limits> | ||
| 8 | #include <memory> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | namespace terraingen { | ||
| 12 | |||
| 13 | /// What a size-frequency distribution's values measure. | ||
| 14 | enum class SfdKind { | ||
| 15 | Density, ///< Cumulative count per m^2 (an equilibrium or present-day population). | ||
| 16 | ProductionRate, ///< Cumulative count per m^2 per Gyr (a production function). | ||
| 17 | }; | ||
| 18 | |||
| 19 | /// Diameters in metres where a distribution is defined: (`min`, `max`], or with `min` = 0 all | ||
| 20 | /// positive diameters up to `max`. | ||
| 21 | struct DiameterRange { | ||
| 22 | double min = 0.0; ///< Lower bound in metres, >= 0. | ||
| 23 | double max = std::numeric_limits<double>::infinity(); ///< Upper bound in metres, > `min`. | ||
| 24 | |||
| 25 | /// Whether `diameter` (metres) is positive and within [`min`, `max`]. | ||
| 26 | 21616671 | bool Contains(double diameter) const { | |
| 27 | 21616671 | return diameter > 0.0 && diameter >= min && diameter <= max; | |
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | /// A cumulative size-frequency distribution N(>=D) of craters or boulders. | ||
| 32 | /// | ||
| 33 | /// Diameters are in metres and counts per m^2 (or per m^2 per Gyr; see `Kind`). Distributions | ||
| 34 | /// decrease monotonically. Evaluating outside `ValidRange` throws unless extrapolation is allowed. | ||
| 35 | class SizeFrequencyDistribution { | ||
| 36 | public: | ||
| 37 | 104764 | virtual ~SizeFrequencyDistribution() = default; | |
| 38 | |||
| 39 | /// What the values measure. | ||
| 40 | 376196 | SfdKind Kind() const { | |
| 41 | 376196 | return kind_; | |
| 42 | } | ||
| 43 | |||
| 44 | /// The diameters in metres where the distribution may be evaluated without extrapolation. | ||
| 45 | 4162 | const DiameterRange& ValidRange() const { | |
| 46 | 4162 | return range_; | |
| 47 | } | ||
| 48 | |||
| 49 | /// Whether evaluation outside `ValidRange` is allowed. | ||
| 50 | 2424 | bool AllowsExtrapolation() const { | |
| 51 | 2424 | return extrapolate_; | |
| 52 | } | ||
| 53 | |||
| 54 | /// Permits or forbids evaluation at positive diameters outside `ValidRange`. | ||
| 55 | 512 | void SetAllowExtrapolation(bool allow) { | |
| 56 | 512 | extrapolate_ = allow; | |
| 57 | 512 | } | |
| 58 | |||
| 59 | /// N(>=D), the count per m^2 (or per m^2 per Gyr) of features at least `diameter` metres | ||
| 60 | /// across. | ||
| 61 | /// @throws std::domain_error if `diameter` is not positive and finite, or is outside the valid | ||
| 62 | /// range without extrapolation. | ||
| 63 | double Cumulative(double diameter) const; | ||
| 64 | |||
| 65 | /// -dN/dD at `diameter` metres, per m^2 per metre (or per m^2 per Gyr per metre). | ||
| 66 | /// @throws std::domain_error under the same conditions as `Cumulative`. | ||
| 67 | double Incremental(double diameter) const; | ||
| 68 | |||
| 69 | /// Expected number of features with diameter in [`dMin`, `dMax`), area * (N(dMin) - N(dMax)). | ||
| 70 | /// @param area Area in m^2 (or m^2 Gyr for a production rate), finite and >= 0. | ||
| 71 | /// @param dMin Smallest diameter in metres. | ||
| 72 | /// @param dMax Largest diameter in metres; may be infinite. | ||
| 73 | /// @throws std::invalid_argument unless 0 < `dMin` < `dMax` and `area` >= 0 is finite. | ||
| 74 | /// @throws std::domain_error if a diameter is outside the valid range without extrapolation, | ||
| 75 | /// or `dMax` is infinite and the valid range is bounded. | ||
| 76 | double ExpectedCount(double area, double dMin, double dMax) const; | ||
| 77 | |||
| 78 | /// Draws a diameter in metres in [`dMin`, `dMax`) by inverting N(>=D); `dMax` may be infinite. | ||
| 79 | /// @throws std::invalid_argument unless 0 < `dMin` < `dMax`, or if N(dMin) = N(dMax). | ||
| 80 | /// @throws std::domain_error under the same conditions as `ExpectedCount`. | ||
| 81 | double SampleDiameter(RandomStream& random, double dMin, double dMax) const; | ||
| 82 | |||
| 83 | protected: | ||
| 84 | /// Sets the kind and valid range. | ||
| 85 | /// @throws std::invalid_argument unless 0 <= `range.min` < `range.max`. | ||
| 86 | SizeFrequencyDistribution(SfdKind kind, DiameterRange range); | ||
| 87 | |||
| 88 | /// N(>=D) at a positive `diameter` in metres, without range checks. | ||
| 89 | virtual double CumulativeAt(double diameter) const = 0; | ||
| 90 | |||
| 91 | /// -dN/dD without range checks; defaults to a central difference in log diameter. | ||
| 92 | virtual double IncrementalAt(double diameter) const; | ||
| 93 | |||
| 94 | /// Diameter D in metres in [`dMin`, `dMax`] with N(>=D) = `target`; defaults to bisection in | ||
| 95 | /// log diameter. | ||
| 96 | /// @param target Cumulative count with N(dMax) <= `target` <= N(dMin). | ||
| 97 | /// @param dMax Largest diameter in metres; may be infinite. | ||
| 98 | virtual double InvertCumulative(double target, double dMin, double dMax) const; | ||
| 99 | |||
| 100 | /// Unchecked `CumulativeAt` of another distribution, for distributions built from others. | ||
| 101 | 43572433 | static double CumulativeOf(const SizeFrequencyDistribution& distribution, double diameter) { | |
| 102 | 43572433 | return distribution.CumulativeAt(diameter); | |
| 103 | } | ||
| 104 | |||
| 105 | /// Unchecked `IncrementalAt` of another distribution. | ||
| 106 | 2428657 | static double IncrementalOf(const SizeFrequencyDistribution& distribution, double diameter) { | |
| 107 | 2428657 | return distribution.IncrementalAt(diameter); | |
| 108 | } | ||
| 109 | |||
| 110 | /// Unchecked `InvertCumulative` of another distribution. | ||
| 111 | 165453 | static double InvertCumulativeOf(const SizeFrequencyDistribution& distribution, double target, | |
| 112 | double dMin, double dMax) { | ||
| 113 | 165453 | return distribution.InvertCumulative(target, dMin, dMax); | |
| 114 | } | ||
| 115 | |||
| 116 | private: | ||
| 117 | void CheckDiameter(double diameter) const; | ||
| 118 | |||
| 119 | SfdKind kind_; | ||
| 120 | DiameterRange range_; | ||
| 121 | bool extrapolate_ = false; | ||
| 122 | }; | ||
| 123 | |||
| 124 | } // namespace terraingen | ||
| 125 |