File indexing completed on 2025-01-18 09:59:01
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032 #ifndef QSS_Stepper_HH
0033 #define QSS_Stepper_HH
0034
0035 #include "G4FieldTrack.hh"
0036 #include "G4FieldUtils.hh"
0037 #include "G4LineSection.hh"
0038 #include "G4MagIntegratorStepper.hh"
0039 #include "G4QSS2.hh"
0040 #include "G4QSS3.hh"
0041 #include "G4QSSDriver.hh"
0042 #include "G4QSSMessenger.hh"
0043 #include "G4VIntegrationDriver.hh"
0044 #include "G4qss_misc.hh"
0045
0046 #include <cmath>
0047 #include <cassert>
0048
0049
0050 #define QSS_MAX_SUBSTEPS 1000
0051
0052 template <class QSS>
0053 class G4QSStepper : public G4MagIntegratorStepper
0054 {
0055 public:
0056
0057 G4QSStepper(G4EquationOfMotion* EqRhs,
0058 G4int numberOfVariables = 6,
0059 G4bool primary = true);
0060 ~G4QSStepper() override;
0061
0062 void Stepper(const G4double y[],
0063 const G4double dydx[],
0064 G4double h,
0065 G4double yout[],
0066 G4double yerr[]) override;
0067
0068 void Stepper(const G4double yInput[],
0069 const G4double dydx[],
0070 G4double hstep,
0071 G4double yOutput[],
0072 G4double yError[],
0073 G4double dydxOutput[]);
0074
0075
0076
0077 inline void SetupInterpolation() {}
0078 inline void Interpolate(G4double tau, G4double yOut[]);
0079
0080 G4double DistChord() const override;
0081
0082 G4int IntegratorOrder() const override { return method->order(); }
0083
0084 void reset(const G4FieldTrack* track);
0085
0086 void SetPrecision(G4double dq_rel, G4double dq_min);
0087
0088
0089 static G4QSStepper<G4QSS2>* build_QSS2(G4EquationOfMotion* EqRhs,
0090 G4int numberOfVariables = 6,
0091 G4bool primary = true);
0092
0093 static G4QSStepper<G4QSS3>* build_QSS3(G4EquationOfMotion* EqRhs,
0094 G4int numberOfVariables = 6,
0095 G4bool primary = true);
0096
0097 inline G4EquationOfMotion* GetSpecificEquation() { return GetEquationOfMotion(); }
0098
0099 inline const field_utils::State& GetYOut() const { return fyOut; }
0100
0101 inline G4double GetLastStepLength() { return fLastStepLength; }
0102
0103 private:
0104
0105 G4QSStepper(QSS* method,
0106 G4EquationOfMotion* EqRhs,
0107 G4int numberOfVariables = 6,
0108 G4bool primary = true);
0109
0110 void initialize_data_structs();
0111 static QSS_simulator build_simulator();
0112
0113 inline void update_field();
0114 inline void save_substep(G4double time, G4double length);
0115
0116 inline void realloc_substeps();
0117 inline void get_state_from_poly(G4double* x, G4double* tx,
0118 G4double time, G4double* state);
0119
0120 inline void recompute_derivatives(int index);
0121 inline void update_time();
0122
0123 inline G4double get_coeff() { return fCoeff_local; }
0124
0125 inline void set_coeff(G4double coeff) { fCoeff_local = coeff; }
0126
0127 inline void set_charge(G4double q)
0128 {
0129 f_charge_c2 = q * cLight_local * cLight_local;
0130 }
0131
0132 inline G4double get_qc2() { return f_charge_c2; }
0133
0134 inline void set_mg() { fMassGamma = f_mass * fGamma2; }
0135
0136 inline void set_gamma2(G4double gamma2) { fGamma2 = gamma2; }
0137 inline void set_velocity(G4double v) { fVelocity = v; }
0138
0139 inline void velocity_to_momentum(G4double* state);
0140
0141 inline void set_gamma(G4double p_sq)
0142 {
0143 set_gamma2(std::sqrt(p_sq / (f_mass * f_mass) + 1));
0144 set_mg();
0145 set_coeff(get_qc2() / fMassGamma);
0146 }
0147
0148 private:
0149
0150 QSS_simulator simulator;
0151 QSS* method;
0152
0153
0154
0155 G4double fLastStepLength;
0156 field_utils::State fyIn, fyOut;
0157
0158 G4double f_mass;
0159 static constexpr G4double cLight_local = 299.792458;
0160 G4double f_charge_c2;
0161 G4double fMassGamma;
0162 G4double fGamma2;
0163 G4double fCoeff_local;
0164 G4double fVelocity;
0165 };
0166
0167 using G4QSStepper_QSS2 = G4QSStepper<G4QSS2>;
0168 using G4QSStepper_QSS3 = G4QSStepper<G4QSS3>;
0169
0170 template <class QSS>
0171 inline G4QSStepper<QSS>::G4QSStepper(QSS* qss, G4EquationOfMotion* EqRhs,
0172 G4int noIntegrationVariables, G4bool)
0173 : G4MagIntegratorStepper(EqRhs, noIntegrationVariables),
0174 simulator(qss->getSimulator()),
0175 method(qss)
0176 {
0177 SetIsQSS(true);
0178 fLastStepLength = -1.0;
0179
0180 f_mass = 0;
0181 f_charge_c2 = 0;
0182 fMassGamma = 0;
0183 fGamma2 = 0;
0184 fCoeff_local = 0;
0185 fVelocity = 0;
0186
0187 this->initialize_data_structs();
0188 this->SetPrecision(1e-4, 1e-7);
0189 }
0190
0191 template <class QSS>
0192 inline G4QSStepper<QSS>::~G4QSStepper()
0193 {
0194 for (auto & i : simulator->SD) { free(i); }
0195
0196 free(SUBSTEPS(this->simulator));
0197 free(this->simulator);
0198 }
0199
0200 template <class QSS>
0201 inline void G4QSStepper<QSS>::Stepper(const G4double yInput[],
0202 const G4double dydx[],
0203 G4double hstep,
0204 G4double yOutput[],
0205 G4double yError[],
0206 G4double [])
0207 {
0208 Stepper(yInput, dydx, hstep, yOutput, yError);
0209 }
0210
0211 template <class QSS>
0212 inline void G4QSStepper<QSS>::update_time()
0213 {
0214 auto* const sim = this->simulator;
0215
0216 sim->time = sim->nextStateTime[0];
0217 sim->minIndex = 0;
0218
0219 if (sim->nextStateTime[1] < sim->time) {
0220 sim->time = sim->nextStateTime[1];
0221 sim->minIndex = 1;
0222 }
0223 if (sim->nextStateTime[2] < sim->time) {
0224 sim->time = sim->nextStateTime[2];
0225 sim->minIndex = 2;
0226 }
0227 if (sim->nextStateTime[3] < sim->time) {
0228 sim->time = sim->nextStateTime[3];
0229 sim->minIndex = 3;
0230 }
0231 if (sim->nextStateTime[4] < sim->time) {
0232 sim->time = sim->nextStateTime[4];
0233 sim->minIndex = 4;
0234 }
0235 if (sim->nextStateTime[5] < sim->time) {
0236 sim->time = sim->nextStateTime[5];
0237 sim->minIndex = 5;
0238 }
0239 }
0240
0241 template <class QSS>
0242 inline void G4QSStepper<QSS>::Stepper(const G4double yInput[],
0243 const G4double [],
0244 G4double max_length,
0245 G4double yOut[],
0246 G4double[] )
0247 {
0248 G4double elapsed;
0249 G4double t, prev_time = 0;
0250 G4double length = 0.;
0251 G4int index;
0252
0253 const G4int coeffs = method->order() + 1;
0254 G4double* tq = simulator->tq;
0255 G4double* tx = simulator->tx;
0256 G4double* dQRel = simulator->dQRel;
0257 G4double* dQMin = simulator->dQMin;
0258 G4double* lqu = simulator->lqu;
0259 G4double* x = simulator->x;
0260 G4int** SD = simulator->SD;
0261 G4int cf0, infCf0;
0262
0263 CUR_SUBSTEP(simulator) = 0;
0264
0265 this->save_substep(0, length);
0266
0267 this->update_time();
0268 t = simulator->time;
0269 index = simulator->minIndex;
0270
0271 while (length < max_length && t < Qss_misc::INF && CUR_SUBSTEP(simulator) < QSS_MAX_SUBSTEPS) {
0272 cf0 = index * coeffs;
0273 elapsed = t - tx[index];
0274 method->advance_time_x(cf0, elapsed);
0275 tx[index] = t;
0276 lqu[index] = dQRel[index] * std::fabs(x[cf0]);
0277 if (lqu[index] < dQMin[index]) {
0278 lqu[index] = dQMin[index];
0279 }
0280 method->update_quantized_state(index);
0281 tq[index] = t;
0282 method->next_time(index, t);
0283 for (G4int i = 0; i < 3; i++) {
0284 G4int j = SD[index][i];
0285 elapsed = t - tx[j];
0286 infCf0 = j * coeffs;
0287 if (elapsed > 0) {
0288 x[infCf0] = method->evaluate_x_poly(infCf0, elapsed, x);
0289 tx[j] = t;
0290 }
0291 }
0292
0293 this->update_field();
0294 this->recompute_derivatives(index);
0295 method->recompute_next_times(SD[index], t);
0296
0297 if (t > prev_time) {
0298 length += fVelocity * (t - prev_time);
0299 if (length <= max_length) { this->save_substep(t, length); }
0300 else { break; }
0301 }
0302
0303 this->update_time();
0304 prev_time = t;
0305 t = simulator->time;
0306 index = simulator->minIndex;
0307 }
0308
0309 if(CUR_SUBSTEP(simulator) >= QSS_MAX_SUBSTEPS) {
0310 max_length = length;
0311 }
0312
0313 auto* const substep = &LAST_SUBSTEP_STRUCT(simulator);
0314 t = substep->start_time + (max_length - substep->len) / fVelocity;
0315
0316 this->get_state_from_poly(substep->x, substep->tx, t, yOut);
0317
0318 velocity_to_momentum(yOut);
0319
0320 const G4int numberOfVariables = GetNumberOfVariables();
0321 for (G4int i = 0; i < numberOfVariables; ++i) {
0322
0323 fyIn[i] = yInput[i];
0324 fyOut[i] = yOut[i];
0325 }
0326
0327 fLastStepLength = max_length;
0328 }
0329
0330 template<class QSS>
0331 inline G4double G4QSStepper<QSS>::DistChord() const
0332 {
0333 G4double yMid[6];
0334 const_cast<G4QSStepper<QSS>*>(this)->Interpolate(0.5, yMid);
0335
0336 const G4ThreeVector begin = makeVector(fyIn, field_utils::Value3D::Position);
0337 const G4ThreeVector end = makeVector(fyOut, field_utils::Value3D::Position);
0338 const G4ThreeVector mid = makeVector(yMid, field_utils::Value3D::Position);
0339
0340 return G4LineSection::Distline(mid, begin, end);
0341 }
0342
0343 template <class QSS>
0344 inline void G4QSStepper<QSS>::Interpolate(G4double tau, G4double yOut[])
0345 {
0346 G4double length = tau * fLastStepLength;
0347 G4int idx = 0, j = LAST_SUBSTEP(simulator);
0348 G4double end_time;
0349
0350 if (j >= 15) {
0351 G4int i = 0, k = j;
0352 idx = j >> 1;
0353 while (idx < k && i < j - 1) {
0354 if (length < SUBSTEP_LEN(simulator, idx)) {
0355 j = idx;
0356 } else if (length >= SUBSTEP_LEN(simulator, idx + 1)) {
0357 i = idx;
0358 } else {
0359 break;
0360 }
0361
0362 idx = (i + j) >> 1;
0363 }
0364 }
0365 else {
0366 for (; idx < j && length >= SUBSTEP_LEN(simulator, idx + 1); idx++) {;}
0367 }
0368
0369 auto* const substep = &SUBSTEP_STRUCT(simulator, idx);
0370 end_time = substep->start_time + (length - substep->len) / fVelocity;
0371
0372 this->get_state_from_poly(substep->x, substep->tx, end_time, yOut);
0373
0374 velocity_to_momentum(yOut);
0375 }
0376
0377 template <class QSS>
0378 inline void G4QSStepper<QSS>::reset(const G4FieldTrack* track)
0379 {
0380 using Qss_misc::PXidx;
0381 using Qss_misc::PYidx;
0382 using Qss_misc::PZidx;
0383 using Qss_misc::VXidx;
0384 using Qss_misc::VYidx;
0385 using Qss_misc::VZidx;
0386
0387 G4ThreeVector pos = track->GetPosition();
0388 G4ThreeVector momentum = track->GetMomentum();
0389
0390 f_mass = track->GetRestMass();
0391 set_charge(track->GetCharge());
0392 set_gamma(momentum.mag2());
0393 G4double c_mg = cLight_local / fMassGamma;
0394 set_velocity(momentum.mag() * c_mg);
0395
0396 method->reset_state(PXidx, pos.getX());
0397 method->reset_state(PYidx, pos.getY());
0398 method->reset_state(PZidx, pos.getZ());
0399
0400 method->reset_state(VXidx, momentum.getX() * c_mg);
0401 method->reset_state(VYidx, momentum.getY() * c_mg);
0402 method->reset_state(VZidx, momentum.getZ() * c_mg);
0403
0404 this->update_field();
0405 method->full_definition(get_coeff());
0406
0407 method->recompute_all_state_times(0);
0408
0409 simulator->time = 0;
0410 }
0411
0412 template <class QSS>
0413 inline void G4QSStepper<QSS>::SetPrecision(G4double dq_rel, G4double dq_min)
0414 {
0415 G4double* dQMin = simulator->dQMin;
0416 G4double* dQRel = simulator->dQRel;
0417 G4int n_vars = simulator->states;
0418
0419 if (dq_min <= 0) { dq_min = dq_rel * 1e-3; }
0420
0421 for (G4int i = 0; i < n_vars; ++i) {
0422 dQRel[i] = dq_rel;
0423 dQMin[i] = dq_min;
0424 }
0425 }
0426
0427 template <class QSS>
0428 inline void G4QSStepper<QSS>::initialize_data_structs()
0429 {
0430 auto sim = this->simulator;
0431 auto states = (G4int*)calloc(Qss_misc::VAR_IDX_END, sizeof(G4int));
0432
0433 sim->states = Qss_misc::VAR_IDX_END;
0434 sim->it = 0.;
0435
0436 for (unsigned int i = 0; i < Qss_misc::VAR_IDX_END; i++) {
0437 sim->SD[i] = (G4int*)malloc(3 * sizeof(G4int));
0438 }
0439
0440 sim->SD[0][states[0]++] = 3;
0441 sim->SD[0][states[0]++] = 4;
0442 sim->SD[0][states[0]++] = 5;
0443
0444 sim->SD[1][states[1]++] = 3;
0445 sim->SD[1][states[1]++] = 4;
0446 sim->SD[1][states[1]++] = 5;
0447
0448 sim->SD[2][states[2]++] = 3;
0449 sim->SD[2][states[2]++] = 4;
0450 sim->SD[2][states[2]++] = 5;
0451
0452 sim->SD[3][states[3]++] = 0;
0453 sim->SD[3][states[3]++] = 4;
0454 sim->SD[3][states[3]++] = 5;
0455
0456 sim->SD[4][states[4]++] = 1;
0457 sim->SD[4][states[4]++] = 3;
0458 sim->SD[4][states[4]++] = 5;
0459
0460 sim->SD[5][states[5]++] = 2;
0461 sim->SD[5][states[5]++] = 3;
0462 sim->SD[5][states[5]++] = 4;
0463
0464 free(states);
0465 }
0466
0467 template <class QSS>
0468 inline QSS_simulator G4QSStepper<QSS>::build_simulator()
0469 {
0470 QSS_simulator sim = (QSS_simulator)malloc(sizeof(*sim));
0471 MAX_SUBSTEP(sim) = Qss_misc::MIN_SUBSTEPS;
0472 SUBSTEPS(sim) = (QSSSubstep)malloc(Qss_misc::MIN_SUBSTEPS * sizeof(*SUBSTEPS(sim)));
0473 return sim;
0474 }
0475
0476 template <class QSS>
0477 inline void G4QSStepper<QSS>::recompute_derivatives(G4int index)
0478 {
0479 const G4int coeffs = method->order() + 1;
0480 G4double e;
0481 G4int idx = 0;
0482
0483 e = simulator->time - simulator->tq[0];
0484 if (likely(e > 0)) { method->advance_time_q(idx, e); }
0485 simulator->tq[0] = simulator->time;
0486
0487 idx += coeffs;
0488 e = simulator->time - simulator->tq[1];
0489 if (likely(e > 0)) { method->advance_time_q(idx, e); }
0490 simulator->tq[1] = simulator->time;
0491
0492 idx += coeffs;
0493 e = simulator->time - simulator->tq[2];
0494 if (likely(e > 0)) { method->advance_time_q(idx, e); }
0495 simulator->tq[2] = simulator->time;
0496
0497 idx += coeffs;
0498 e = simulator->time - simulator->tq[3];
0499 if (likely(e > 0)) { method->advance_time_q(idx, e); }
0500 simulator->tq[3] = simulator->time;
0501
0502 idx += coeffs;
0503 e = simulator->time - simulator->tq[4];
0504 if (likely(e > 0)) { method->advance_time_q(idx, e); }
0505 simulator->tq[4] = simulator->time;
0506
0507 idx += coeffs;
0508 e = simulator->time - simulator->tq[5];
0509 if (likely(e > 0)) { method->advance_time_q(idx, e); }
0510 simulator->tq[5] = simulator->time;
0511
0512 method->dependencies(index, get_coeff());
0513 }
0514
0515 template <class QSS>
0516 inline void G4QSStepper<QSS>::update_field()
0517 {
0518 using Qss_misc::PXidx;
0519 using Qss_misc::PYidx;
0520 using Qss_misc::PZidx;
0521
0522 const G4int order1 = method->order() + 1;
0523 G4double* const _field = simulator->alg;
0524 G4double* const _point = _field + order1;
0525
0526 _point[PXidx] = simulator->x[PXidx];
0527 _point[PYidx] = simulator->x[PYidx * order1];
0528 _point[PZidx] = simulator->x[PZidx * order1];
0529
0530 this->GetEquationOfMotion()->GetFieldValue(_point, _field);
0531 }
0532
0533 template <class QSS>
0534 inline void G4QSStepper<QSS>::save_substep(G4double time, G4double length)
0535 {
0536 memcpy(CUR_SUBSTEP_X(simulator), simulator->x,
0537 (Qss_misc::VAR_IDX_END * (Qss_misc::MAX_QSS_STEPPER_ORDER + 2)) * sizeof(G4double));
0538
0539 CUR_SUBSTEP_START(simulator) = time;
0540 CUR_SUBSTEP_LEN(simulator) = length;
0541 CUR_SUBSTEP(simulator)++;
0542
0543 if (unlikely(CUR_SUBSTEP(simulator) == MAX_SUBSTEP(simulator))) {
0544 this->realloc_substeps();
0545 }
0546 }
0547
0548 template <class QSS>
0549 inline void G4QSStepper<QSS>::realloc_substeps()
0550 {
0551 const G4int prev_index = MAX_SUBSTEP(simulator), new_index = 2 * prev_index;
0552
0553 MAX_SUBSTEP(simulator) = new_index;
0554 SUBSTEPS(simulator) =
0555 (QSSSubstep)realloc(SUBSTEPS(simulator), new_index * sizeof(*SUBSTEPS(simulator)));
0556 }
0557
0558 template <class QSS>
0559 inline void G4QSStepper<QSS>::get_state_from_poly(
0560 G4double* x, G4double* tx, G4double time, G4double* state)
0561 {
0562 unsigned int coeff_index = 0, i;
0563 const unsigned int x_order = method->order(), x_order1 = x_order + 1;
0564
0565 for (i = 0; i < Qss_misc::VAR_IDX_END; ++i) {
0566 assert(tx[i] <= time);
0567 state[i] = method->evaluate_x_poly(coeff_index, time - tx[i], x);
0568 coeff_index += x_order1;
0569 }
0570 }
0571
0572 template <class QSS>
0573 inline void G4QSStepper<QSS>::velocity_to_momentum(G4double* state)
0574 {
0575 using Qss_misc::VXidx;
0576 using Qss_misc::VYidx;
0577 using Qss_misc::VZidx;
0578 G4double coeff = fMassGamma / cLight_local;
0579
0580 state[VXidx] *= coeff;
0581 state[VYidx] *= coeff;
0582 state[VZidx] *= coeff;
0583 }
0584
0585 #endif