File indexing completed on 2025-09-17 08:53:38
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include <cmath>
0010
0011 #include "corecel/Macros.hh"
0012 #include "corecel/Types.hh"
0013 #include "corecel/math/Algorithms.hh"
0014 #include "corecel/math/NumericLimits.hh"
0015 #include "corecel/math/SoftEqual.hh"
0016
0017 #include "FieldDriverOptions.hh"
0018 #include "Types.hh"
0019
0020 #include "detail/FieldUtils.hh"
0021
0022 namespace celeritas
0023 {
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059 template<class IntegratorT>
0060 class FieldSubstepper
0061 {
0062 public:
0063
0064 inline CELER_FUNCTION FieldSubstepper(FieldDriverOptions const& options,
0065 IntegratorT&& integrate);
0066
0067
0068 inline CELER_FUNCTION Substep operator()(real_type step,
0069 OdeState const& state);
0070
0071
0072
0073
0074
0075 inline CELER_FUNCTION Substep accurate_advance(real_type step,
0076 OdeState const& state,
0077 real_type hinitial) const;
0078
0079
0080
0081 CELER_FUNCTION short int max_substeps() const
0082 {
0083 return options_.max_substeps;
0084 }
0085
0086 CELER_FUNCTION real_type minimum_step() const
0087 {
0088 return options_.minimum_step;
0089 }
0090
0091
0092 CELER_FUNCTION real_type delta_intersection() const
0093 {
0094 return options_.delta_intersection;
0095 }
0096
0097 private:
0098
0099
0100
0101 FieldDriverOptions const& options_;
0102
0103
0104 IntegratorT integrate_;
0105
0106
0107 real_type max_chord_{numeric_limits<real_type>::infinity()};
0108
0109
0110
0111
0112 struct ChordSearch
0113 {
0114 Substep end;
0115 real_type err_sq;
0116 };
0117
0118 struct Integration
0119 {
0120 Substep end;
0121 real_type proposed_length;
0122 };
0123
0124
0125
0126
0127 inline CELER_FUNCTION ChordSearch
0128 find_next_chord(real_type step, OdeState const& state) const;
0129
0130
0131 inline CELER_FUNCTION Integration
0132 integrate_step(real_type step, OdeState const& state) const;
0133
0134
0135 inline CELER_FUNCTION Integration one_good_step(real_type step,
0136 OdeState const& state) const;
0137
0138
0139 inline CELER_FUNCTION real_type new_step_scale(real_type error_sq) const;
0140
0141
0142
0143 static CELER_CONSTEXPR_FUNCTION real_type half() { return 0.5; }
0144 };
0145
0146
0147
0148
0149 template<class IntegratorT>
0150 CELER_FUNCTION FieldSubstepper(FieldDriverOptions const&,
0151 IntegratorT&&) -> FieldSubstepper<IntegratorT>;
0152
0153
0154
0155
0156
0157
0158
0159 template<class IntegratorT>
0160 CELER_FUNCTION
0161 FieldSubstepper<IntegratorT>::FieldSubstepper(FieldDriverOptions const& options,
0162 IntegratorT&& integrate)
0163 : options_(options)
0164 , integrate_(::celeritas::forward<IntegratorT>(integrate))
0165 {
0166 CELER_EXPECT(options_);
0167 }
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177
0178
0179
0180
0181
0182
0183
0184
0185 template<class IntegratorT>
0186 CELER_FUNCTION Substep
0187 FieldSubstepper<IntegratorT>::operator()(real_type step, OdeState const& state)
0188 {
0189 if (step <= options_.minimum_step)
0190 {
0191
0192 Substep result;
0193 result.state = integrate_(step, state).end_state;
0194 result.length = step;
0195 return result;
0196 }
0197
0198
0199
0200 ChordSearch next
0201 = this->find_next_chord(celeritas::min(step, max_chord_), state);
0202 CELER_ASSERT(next.end.length <= step);
0203 if (next.end.length < step)
0204 {
0205
0206
0207 max_chord_ = next.end.length * (1 / options_.min_chord_shrink);
0208 }
0209
0210 if (next.err_sq > 1)
0211 {
0212
0213
0214 real_type next_step = step * this->new_step_scale(next.err_sq);
0215 next.end = this->accurate_advance(next.end.length, state, next_step);
0216 }
0217
0218 CELER_ENSURE(next.end.length > 0 && next.end.length <= step);
0219 return next.end;
0220 }
0221
0222
0223
0224
0225
0226 template<class IntegratorT>
0227 CELER_FUNCTION auto FieldSubstepper<IntegratorT>::find_next_chord(
0228 real_type step, OdeState const& state) const -> ChordSearch
0229 {
0230 bool succeeded = false;
0231 auto remaining_steps = options_.max_nsteps;
0232 FieldIntegration integrated;
0233
0234 do
0235 {
0236
0237 integrated = integrate_(step, state);
0238
0239
0240
0241 real_type dchord = detail::distance_chord(
0242 state.pos, integrated.mid_state.pos, integrated.end_state.pos);
0243
0244 if (dchord > options_.delta_chord + options_.dchord_tol)
0245 {
0246
0247 real_type scale_step = max(std::sqrt(options_.delta_chord / dchord),
0248 options_.min_chord_shrink);
0249 step *= scale_step;
0250 }
0251 else
0252 {
0253 succeeded = true;
0254 }
0255 } while (!succeeded && --remaining_steps > 0);
0256
0257
0258 ChordSearch result;
0259 result.end.length = step;
0260 result.end.state = integrated.end_state;
0261 result.err_sq = detail::rel_err_sq(integrated.err_state, step, state.mom)
0262 / ipow<2>(options_.epsilon_rel_max);
0263
0264 return result;
0265 }
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278 template<class IntegratorT>
0279 CELER_FUNCTION Substep FieldSubstepper<IntegratorT>::accurate_advance(
0280 real_type step, OdeState const& state, real_type hinitial) const
0281 {
0282 CELER_ASSERT(step > 0);
0283
0284
0285 real_type end_curve_length = step;
0286
0287
0288
0289
0290
0291 real_type h
0292 = ((hinitial > options_.initial_step_tol * step) && (hinitial < step))
0293 ? hinitial
0294 : step;
0295 real_type h_threshold = options_.epsilon_step * step;
0296
0297
0298 Integration result;
0299 result.end.state = state;
0300
0301
0302 bool succeeded = false;
0303 real_type curve_length = 0;
0304 auto remaining_steps = options_.max_nsteps;
0305
0306 do
0307 {
0308 CELER_ASSERT(h > 0);
0309 result = this->integrate_step(h, result.end.state);
0310
0311 curve_length += result.end.length;
0312
0313 if (h < h_threshold || curve_length >= end_curve_length)
0314 {
0315 succeeded = true;
0316 }
0317 else
0318 {
0319 h = celeritas::min(
0320 celeritas::max(result.proposed_length, options_.minimum_step),
0321 end_curve_length - curve_length);
0322 }
0323 } while (!succeeded && --remaining_steps > 0);
0324
0325
0326
0327 CELER_ENSURE(curve_length > 0
0328 && (curve_length <= step || soft_equal(curve_length, step)));
0329 result.end.length = min(curve_length, step);
0330 return result.end;
0331 }
0332
0333
0334
0335
0336
0337
0338
0339 template<class IntegratorT>
0340 CELER_FUNCTION auto FieldSubstepper<IntegratorT>::integrate_step(
0341 real_type step, OdeState const& state) const -> Integration
0342 {
0343 CELER_EXPECT(step > 0);
0344
0345
0346 Integration result;
0347
0348 if (step > options_.minimum_step)
0349 {
0350 result = this->one_good_step(step, state);
0351 }
0352 else
0353 {
0354
0355 FieldIntegration integrated = integrate_(step, state);
0356
0357
0358 result.end.state = integrated.end_state;
0359 result.end.length = step;
0360
0361
0362 real_type err_sq
0363 = detail::rel_err_sq(integrated.err_state, step, state.mom)
0364 / ipow<2>(options_.epsilon_rel_max);
0365 result.proposed_length = step * this->new_step_scale(err_sq);
0366 }
0367
0368 return result;
0369 }
0370
0371
0372
0373
0374
0375
0376 template<class IntegratorT>
0377 CELER_FUNCTION auto FieldSubstepper<IntegratorT>::one_good_step(
0378 real_type step, OdeState const& state) const -> Integration
0379 {
0380
0381 bool succeeded = false;
0382 size_type remaining_steps = options_.max_nsteps;
0383 real_type err_sq;
0384 FieldIntegration integrated;
0385
0386 do
0387 {
0388 integrated = integrate_(step, state);
0389
0390 err_sq = detail::rel_err_sq(integrated.err_state, step, state.mom)
0391 / ipow<2>(options_.epsilon_rel_max);
0392
0393 if (err_sq > 1)
0394 {
0395
0396 step *= max(this->new_step_scale(err_sq),
0397 options_.max_stepping_decrease);
0398 }
0399 else
0400 {
0401
0402 succeeded = true;
0403 }
0404 } while (!succeeded && --remaining_steps > 0);
0405
0406
0407 Integration result;
0408 result.end.state = integrated.end_state;
0409 result.end.length = step;
0410 result.proposed_length
0411 = step
0412 * min(this->new_step_scale(err_sq), options_.max_stepping_increase);
0413
0414 return result;
0415 }
0416
0417
0418
0419
0420
0421 template<class IntegratorT>
0422 CELER_FUNCTION real_type
0423 FieldSubstepper<IntegratorT>::new_step_scale(real_type err_sq) const
0424 {
0425 CELER_ASSERT(err_sq >= 0);
0426 return options_.safety
0427 * fastpow(err_sq,
0428 half() * (err_sq > 1 ? options_.pshrink : options_.pgrow));
0429 }
0430
0431
0432 }