GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
201 / 206
97.6%
Branches:
133 / 206
64.6%
Functions:
23 / 23
100.0%

physics/regolith.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file regolith.cpp
3 /// @brief 1-D regolith heat conduction and diurnal tables.
4 #include "ernie/physics/regolith.h"
5
6 #include "ernie/physics/constants.h"
7 #include "ernie/util/parallel.h"
8 #include "ernie/util/profiler.h"
9
10 #include <algorithm>
11 #include <cmath>
12 #include <cstddef>
13 #include <functional>
14 #include <numbers>
15 #include <stdexcept>
16 #include <string>
17
18 namespace ernie {
19
20 namespace {
21
22 using std::numbers::pi;
23
24 10644584 double HeatCapacity(const RegolithProperties& regolith, double kelvin) {
25 10644584 const auto& c = regolith.heatCapacity;
26 10644584 return c[0] + kelvin * (c[1] + kelvin * (c[2] + kelvin * (c[3] + kelvin * c[4])));
27 }
28
29 /// Bond albedo at incidence `incidence` (radians).
30 256806 double Albedo(const RegolithProperties& regolith, double incidence) {
31 256806 const double a = incidence / (0.25 * pi);
32 256806 const double b = incidence / (0.5 * pi);
33 256806 return regolith.albedo + regolith.albedoA * a * a * a + regolith.albedoB * std::pow(b, 8.0);
34 }
35
36 /// The model's nodes: the surface, then deeper ones each 1.2 times
37 /// further apart, the first a tenth of the surface's diurnal skin
38 /// depth down, the last ten skin depths of the deep regolith's.
39 struct Grid {
40 std::vector<double> depth;
41 std::vector<double> density;
42 std::vector<double> contact; ///< Contact conductivity.
43 std::vector<double> volume; ///< Per unit area, about each node.
44 };
45
46 33 Grid MakeGrid(const RegolithProperties& regolith) {
47 99 const auto skinDepth = [&](double conductivity, double density) {
48 66 const double diffusivity = conductivity / (density * HeatCapacity(regolith, 250.0));
49 66 return std::sqrt(diffusivity * regolith.dayLength / pi);
50 33 };
51 33 const double bottom = 10.0 * skinDepth(regolith.deepConductivity, regolith.deepDensity);
52 33 double step = 0.1 * skinDepth(regolith.surfaceConductivity, regolith.surfaceDensity);
53 33 Grid grid;
54 693 for (double z = 0.0;; z += step, step *= 1.2) {
55
1/2
✓ Branch 1 taken 693 times.
✗ Branch 2 not taken.
693 grid.depth.push_back(z);
56
2/2
✓ Branch 0 taken 660 times.
✓ Branch 1 taken 33 times.
693 if (z >= bottom) {
57 break;
58 }
59 }
60 33 const std::size_t n = grid.depth.size();
61
2/2
✓ Branch 0 taken 693 times.
✓ Branch 1 taken 33 times.
726 for (std::size_t i = 0; i < n; ++i) {
62 693 const double density =
63 693 regolith.deepDensity - (regolith.deepDensity - regolith.surfaceDensity) *
64
1/2
✓ Branch 1 taken 693 times.
✗ Branch 2 not taken.
693 std::exp(-grid.depth[i] / regolith.densityScale);
65
1/2
✓ Branch 1 taken 693 times.
✗ Branch 2 not taken.
693 grid.density.push_back(density);
66 1386 grid.contact.push_back(regolith.deepConductivity -
67 693 (regolith.deepConductivity - regolith.surfaceConductivity) *
68 693 (regolith.deepDensity - density) /
69
1/2
✓ Branch 1 taken 693 times.
✗ Branch 2 not taken.
693 (regolith.deepDensity - regolith.surfaceDensity));
70
2/2
✓ Branch 0 taken 660 times.
✓ Branch 1 taken 33 times.
693 const double above = i > 0 ? grid.depth[i] - grid.depth[i - 1] : 0.0;
71
2/2
✓ Branch 0 taken 660 times.
✓ Branch 1 taken 33 times.
693 const double below = i + 1 < n ? grid.depth[i + 1] - grid.depth[i] : 0.0;
72
1/2
✓ Branch 1 taken 693 times.
✗ Branch 2 not taken.
693 grid.volume.push_back(0.5 * (above + below));
73 }
74 33 return grid;
75 ✗ }
76
77 /// One backward Euler step of `dt` seconds with `absorbed` W/m^2 of
78 /// sunlight: conductivity and heat capacity at the step's start, the
79 /// surface's emission linearised about it.
80 506880 void Step(const RegolithProperties& regolith, const Grid& grid, std::vector<double>& kelvin,
81 double dt, double absorbed) {
82 506880 const std::size_t n = kelvin.size();
83 506880 std::vector<double> conductance(n, 0.0); // node i to i + 1
84
2/2
✓ Branch 0 taken 10137600 times.
✓ Branch 1 taken 506880 times.
10644480 for (std::size_t i = 0; i + 1 < n; ++i) {
85 30412800 const auto conductivity = [&](std::size_t j) {
86 20275200 const double ratio = kelvin[j] / 350.0;
87 20275200 return grid.contact[j] * (1.0 + regolith.radiativeChi * ratio * ratio * ratio);
88 10137600 };
89 10137600 conductance[i] =
90 10137600 0.5 * (conductivity(i) + conductivity(i + 1)) / (grid.depth[i + 1] - grid.depth[i]);
91 }
92
4/8
✓ Branch 1 taken 506880 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 506880 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 506880 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 506880 times.
✗ Branch 11 not taken.
506880 std::vector<double> lower(n), diagonal(n), upper(n), right(n);
93
2/2
✓ Branch 0 taken 10644480 times.
✓ Branch 1 taken 506880 times.
11151360 for (std::size_t i = 0; i < n; ++i) {
94 10644480 const double capacity =
95
2/2
✓ Branch 0 taken 10137600 times.
✓ Branch 1 taken 506880 times.
10644480 grid.density[i] * HeatCapacity(regolith, kelvin[i]) * grid.volume[i] / dt;
96
2/2
✓ Branch 0 taken 10137600 times.
✓ Branch 1 taken 506880 times.
10644480 const double above = i > 0 ? conductance[i - 1] : 0.0;
97 10644480 lower[i] = -above;
98 10644480 upper[i] = -conductance[i];
99 10644480 diagonal[i] = capacity + above + conductance[i];
100 10644480 right[i] = capacity * kelvin[i];
101 }
102 506880 const double emitting = regolith.emissivity * kStefanBoltzmann;
103 506880 const double t3 = kelvin[0] * kelvin[0] * kelvin[0];
104 506880 diagonal[0] += 4.0 * emitting * t3;
105 506880 right[0] += absorbed + 3.0 * emitting * t3 * kelvin[0];
106 506880 right[n - 1] += regolith.geothermalFlux;
107 // Thomas's algorithm.
108
2/2
✓ Branch 0 taken 10137600 times.
✓ Branch 1 taken 506880 times.
10644480 for (std::size_t i = 1; i < n; ++i) {
109 10137600 const double factor = lower[i] / diagonal[i - 1];
110 10137600 diagonal[i] -= factor * upper[i - 1];
111 10137600 right[i] -= factor * right[i - 1];
112 }
113 506880 kelvin[n - 1] = right[n - 1] / diagonal[n - 1];
114
2/2
✓ Branch 0 taken 10137600 times.
✓ Branch 1 taken 506880 times.
10644480 for (std::size_t i = n - 1; i-- > 0;) {
115 10137600 kelvin[i] = (right[i] - upper[i] * kelvin[i + 1]) / diagonal[i];
116 }
117 506880 }
118
119 } // namespace
120
121 34 std::vector<double> DiurnalSurfaceTemperatures(const RegolithProperties& regolith, double latitude,
122 int samples) {
123
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 33 times.
34 if (samples < 2) {
124
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 throw std::invalid_argument("Diurnal temperatures need at least 2 samples");
125 }
126 33 const Grid grid = MakeGrid(regolith);
127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 const double cosLatitude = std::max(std::cos(latitude), 0.0);
128 // Sunlight absorbed at `phase` of the day from local midnight.
129 506913 const auto absorbedAt = [&](double phase) {
130 506880 const double cosIncidence = cosLatitude * std::cos(2.0 * pi * phase - pi);
131
2/2
✓ Branch 0 taken 256806 times.
✓ Branch 1 taken 250074 times.
506880 if (cosIncidence <= 0.0) {
132 return 0.0;
133 }
134 // A bright regolith's albedo would pass 1 at grazing sun.
135
2/2
✓ Branch 1 taken 8954 times.
✓ Branch 2 taken 247852 times.
256806 return (1.0 - std::min(Albedo(regolith, std::acos(cosIncidence)), 1.0)) *
136 256806 regolith.solarIrradiance * cosIncidence;
137 33 };
138 // From about the deep regolith's temperature, a hundred days in
139 // coarse steps settle the layers below the daily swing, then two
140 // in fine steps the swing itself; the last is recorded.
141 33 const double noon = std::pow((1.0 - regolith.albedo) * regolith.solarIrradiance * cosLatitude /
142 33 (regolith.emissivity * kStefanBoltzmann),
143 0.25);
144
4/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 26 times.
✓ Branch 3 taken 33 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 33 times.
✗ Branch 7 not taken.
40 std::vector<double> kelvin(grid.depth.size(), std::max(0.65 * noon, 40.0));
145 66 const auto run = [&](int days, int stepsPerDay, auto&& record) {
146 66 const double dt = regolith.dayLength / stepsPerDay;
147
4/4
auto ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int, auto:1&&)#1}::operator()<ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int)#1}>(int, int, ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int)#1}&&) const:
✓ Branch 0 taken 3300 times.
✓ Branch 1 taken 33 times.
auto ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int, auto:1&&)#1}::operator()<ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int)#2}>(int, int, ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int)#2}&&) const:
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 33 times.
3432 for (int day = 0; day < days; ++day) {
148
4/4
auto ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int, auto:1&&)#1}::operator()<ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int)#1}>(int, int, ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int)#1}&&) const:
✓ Branch 0 taken 316800 times.
✓ Branch 1 taken 3300 times.
auto ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int, auto:1&&)#1}::operator()<ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int)#2}>(int, int, ernie::DiurnalSurfaceTemperatures(ernie::RegolithProperties const&, double, int)::{lambda(int, int)#2}&&) const:
✓ Branch 0 taken 190080 times.
✓ Branch 1 taken 66 times.
510246 for (int s = 0; s < stepsPerDay; ++s) {
149 506880 const double phase = static_cast<double>(s + 1) / stepsPerDay;
150 506880 Step(regolith, grid, kelvin, dt, absorbedAt(phase));
151 190080 record(day, s + 1);
152 }
153 }
154 66 };
155
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 run(100, 96, [](int, int) {});
156 33 const int stride = (2880 + samples - 1) / samples;
157
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 std::vector<double> surface(static_cast<std::size_t>(samples));
158
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 run(2, stride * samples, [&](int day, int step) {
159
4/4
✓ Branch 0 taken 95040 times.
✓ Branch 1 taken 95040 times.
✓ Branch 2 taken 2120 times.
✓ Branch 3 taken 92920 times.
190080 if (day == 1 && step % stride == 0) {
160 2120 surface[static_cast<std::size_t>((step / stride) % samples)] = kelvin[0];
161 }
162 190080 });
163 33 return surface;
164 33 }
165
166 38 double ThermalInertia(const RegolithProperties& regolith) {
167 38 return std::sqrt(regolith.deepConductivity * regolith.deepDensity *
168 38 HeatCapacity(regolith, 250.0));
169 }
170
171 33 RegolithProperties WithThermalInertia(RegolithProperties regolith, double inertia) {
172
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 32 times.
33 if (!(inertia > 0.0)) {
173 1 throw std::invalid_argument("A thermal inertia must be above 0, not " +
174
3/6
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
2 std::to_string(inertia));
175 }
176 32 const double ratio = inertia / ThermalInertia(regolith);
177 32 regolith.surfaceConductivity *= ratio * ratio;
178 32 regolith.deepConductivity *= ratio * ratio;
179 32 return regolith;
180 }
181
182 2092 std::size_t DiurnalTable::At(int latitude, int time, int inertia, int albedo) const {
183 2092 const auto index = [](int value) { return static_cast<std::size_t>(value); };
184 2092 return ((index(latitude) * index(times) + index(time)) * inertias.size() + index(inertia)) *
185 2092 albedos.size() +
186 2092 index(albedo);
187 }
188
189 namespace {
190
191 /// Whether `values` is not empty and strictly ascending.
192 15 bool Ascending(const std::vector<float>& values) {
193
3/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 14 times.
30 return !values.empty() &&
194
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 14 times.
15 std::adjacent_find(values.begin(), values.end(), std::greater_equal<>()) == values.end();
195 }
196
197 } // namespace
198
199 8 void DiurnalTable::Check() const {
200 10 const auto bad = [](const std::string& why) {
201
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 return std::invalid_argument("Diurnal table: " + why);
202 };
203
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 if (latitudes < 2 || times < 2) {
204
0/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ throw bad("needs at least 2 latitudes and times");
205 }
206
3/4
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
8 if (!Ascending(inertias) || !Ascending(albedos)) {
207
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
2 throw bad("inertias and albedos must be given ascending");
208 }
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (!(inertias.front() > 0.0f)) {
210
0/4
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ throw bad("inertias must be above 0");
211 }
212
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
7 if (kelvin.size() != At(latitudes, 0)) {
213
3/6
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 time.
✗ Branch 8 not taken.
3 throw bad("has " + std::to_string(kelvin.size()) + " temperatures for " +
214
2/4
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
4 std::to_string(At(latitudes, 0)) + " entries");
215 }
216 6 }
217
218 1 std::vector<float> DiurnalTable::ModelData() const {
219 1 Check();
220 1 std::vector<float> data{kModelDataTag, static_cast<float>(latitudes), static_cast<float>(times),
221 1 static_cast<float>(inertias.size()),
222 1 static_cast<float>(albedos.size())};
223
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 data.insert(data.end(), inertias.begin(), inertias.end());
224
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 data.insert(data.end(), albedos.begin(), albedos.end());
225
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 data.insert(data.end(), kelvin.begin(), kelvin.end());
226 1 return data;
227 ✗ }
228
229 4 DiurnalTable DiurnalTable::FromModelData(const std::vector<float>& data) {
230 4 constexpr std::size_t kHeader = 5;
231
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
4 if (data.size() < kHeader || data[0] != kModelDataTag) {
232 1 throw std::invalid_argument("Diurnal table data must start with its layout tag (-2), "
233
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 "as DiurnalTable::ModelData writes it");
234 }
235 12 const auto count = [&](std::size_t i) {
236
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 const float value = data[i];
237
4/6
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 8 times.
9 if (!(value >= 0.0f && value < 1e7f) || value != std::floor(value)) {
238 1 throw std::invalid_argument("Diurnal table data has a bad count: " +
239
3/6
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 time.
✗ Branch 9 not taken.
2 std::to_string(value));
240 }
241 8 return static_cast<std::size_t>(value);
242 3 };
243 3 DiurnalTable table;
244
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 table.latitudes = static_cast<int>(count(1));
245
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 table.times = static_cast<int>(count(2));
246
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const std::size_t inertias = count(3);
247
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const std::size_t albedos = count(4);
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (data.size() < kHeader + inertias + albedos) {
249
0/2
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ throw std::invalid_argument("Diurnal table data is cut short");
250 }
251
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const auto first = data.begin() + kHeader;
252
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 table.inertias.assign(first, first + static_cast<long>(inertias));
253
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 table.albedos.assign(first + static_cast<long>(inertias),
254 first + static_cast<long>(inertias + albedos));
255
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 table.kelvin.assign(first + static_cast<long>(inertias + albedos), data.end());
256
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
2 table.Check();
257 1 return table;
258 2 }
259
260 6 DiurnalTable DiurnalTemperatureTable(const RegolithProperties& regolith, int latitudes, int times,
261 std::vector<float> inertias, std::vector<float> albedos) {
262 6 ERNIE_ZONE("Diurnal temperature table");
263 6 DiurnalTable table;
264 6 table.latitudes = latitudes;
265 6 table.times = times;
266 6 table.inertias = inertias.empty()
267
5/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 3 times.
12 ? std::vector<float>{static_cast<float>(ThermalInertia(regolith))}
268 6 : std::move(inertias);
269
4/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
12 table.albedos = albedos.empty() ? std::vector<float>{static_cast<float>(regolith.albedo)}
270 6 : std::move(albedos);
271
3/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 5 times.
6 if (!(table.albedos.front() >= 0.0f && table.albedos.back() < 1.0f)) {
272
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 throw std::invalid_argument("Diurnal table: albedos must lie in [0, 1)");
273 }
274
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 table.kelvin.assign(latitudes >= 2 && times >= 2 ? table.At(latitudes, 0) : 0, 0.0f);
275
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 time.
5 table.Check();
276 // A row a latitude, inertia and albedo, run across threads.
277
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 const std::size_t inertiaCount = table.inertias.size();
278 4 const std::size_t albedoCount = table.albedos.size();
279 4 const std::size_t rows = static_cast<std::size_t>(latitudes) * inertiaCount * albedoCount;
280
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 ParallelLoop(rows, [&](std::size_t r) {
281 31 const auto albedo = static_cast<int>(r % albedoCount);
282 31 const auto inertia = static_cast<int>(r / albedoCount % inertiaCount);
283 31 const auto row = static_cast<int>(r / (albedoCount * inertiaCount));
284 31 RegolithProperties properties =
285 31 WithThermalInertia(regolith, table.inertias[static_cast<std::size_t>(inertia)]);
286 31 properties.albedo = table.albedos[static_cast<std::size_t>(albedo)];
287 31 const std::vector<double> kelvin =
288 31 DiurnalSurfaceTemperatures(properties, 0.5 * pi * row / (latitudes - 1), times);
289
2/2
✓ Branch 0 taken 2072 times.
✓ Branch 1 taken 31 times.
2103 for (int time = 0; time < times; ++time) {
290 2072 table.kelvin[table.At(row, time, inertia, albedo)] =
291 2072 static_cast<float>(kelvin[static_cast<std::size_t>(time)]);
292 }
293 31 });
294 4 return table;
295 8 }
296
297 1 const DiurnalTable& LunarDiurnalTable() {
298
3/6
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
1 static const DiurnalTable table = DiurnalTemperatureTable(RegolithProperties{}, 19, 96);
299 1 return table;
300 }
301
302 } // namespace ernie
303