1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.propagation.semianalytical.dsst.forces;
18
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.TreeMap;
22
23 import org.apache.commons.math3.analysis.differentiation.DerivativeStructure;
24 import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
25 import org.apache.commons.math3.util.FastMath;
26 import org.apache.commons.math3.util.MathUtils;
27 import org.orekit.errors.OrekitException;
28 import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider;
29 import org.orekit.forces.gravity.potential.UnnormalizedSphericalHarmonicsProvider.UnnormalizedSphericalHarmonics;
30 import org.orekit.frames.Frame;
31 import org.orekit.frames.Transform;
32 import org.orekit.propagation.SpacecraftState;
33 import org.orekit.propagation.events.EventDetector;
34 import org.orekit.propagation.semianalytical.dsst.utilities.AuxiliaryElements;
35 import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory;
36 import org.orekit.propagation.semianalytical.dsst.utilities.CoefficientsFactory.MNSKey;
37 import org.orekit.propagation.semianalytical.dsst.utilities.GHmsjPolynomials;
38 import org.orekit.propagation.semianalytical.dsst.utilities.GammaMnsFunction;
39 import org.orekit.propagation.semianalytical.dsst.utilities.JacobiPolynomials;
40 import org.orekit.propagation.semianalytical.dsst.utilities.NewcombOperators;
41 import org.orekit.time.AbsoluteDate;
42
43
44
45
46
47
48
49
50
51
52 class TesseralContribution implements DSSTForceModel {
53
54
55
56
57 private static final double MIN_PERIOD_IN_SECONDS = 864000.;
58
59
60
61
62 private static final double MIN_PERIOD_IN_SAT_REV = 10.;
63
64
65 private final UnnormalizedSphericalHarmonicsProvider provider;
66
67
68 private final Frame bodyFrame;
69
70
71 private final double bodyPeriod;
72
73
74 private final int maxDegree;
75
76
77 private final List<Integer> resOrders;
78
79
80 private final double[] fact;
81
82
83 private int maxEccPow;
84
85
86 private int maxHansen;
87
88
89 private double orbitPeriod;
90
91
92 private double ratio;
93
94
95 private int I;
96
97
98
99 private double a;
100
101 private double k;
102
103 private double h;
104
105 private double q;
106
107 private double p;
108
109 private double lm;
110
111
112 private double ecc;
113
114
115
116 private Vector3D f;
117
118 private Vector3D g;
119
120
121 private double theta;
122
123
124 private double alpha;
125
126 private double beta;
127
128 private double gamma;
129
130
131
132 private double ax2oA;
133
134 private double ooAB;
135
136 private double BoA;
137
138 private double BoABpo;
139
140 private double Co2AB;
141
142 private double moa;
143
144 private double roa;
145
146
147
148
149
150
151 public TesseralContribution(final Frame centralBodyFrame,
152 final double centralBodyRotationRate,
153 final UnnormalizedSphericalHarmonicsProvider provider) {
154
155
156 this.bodyFrame = centralBodyFrame;
157
158
159 this.bodyPeriod = MathUtils.TWO_PI / centralBodyRotationRate;
160
161
162 this.provider = provider;
163 this.maxDegree = provider.getMaxDegree();
164
165
166 this.resOrders = new ArrayList<Integer>();
167 this.maxEccPow = 0;
168 this.maxHansen = 0;
169
170
171 final int maxFact = 2 * maxDegree + 1;
172 this.fact = new double[maxFact];
173 fact[0] = 1;
174 for (int i = 1; i < maxFact; i++) {
175 fact[i] = i * fact[i - 1];
176 }
177
178 }
179
180
181 public void initialize(final AuxiliaryElements aux)
182 throws OrekitException {
183
184
185 orbitPeriod = aux.getKeplerianPeriod();
186
187
188 ratio = orbitPeriod / bodyPeriod;
189
190
191 getResonantTerms();
192
193
194
195
196 final double e = aux.getEcc();
197 if (e <= 0.005) {
198 maxEccPow = 3;
199 } else if (e <= 0.02) {
200 maxEccPow = 4;
201 } else if (e <= 0.1) {
202 maxEccPow = 7;
203 } else if (e <= 0.2) {
204 maxEccPow = 10;
205 } else if (e <= 0.3) {
206 maxEccPow = 12;
207 } else if (e <= 0.4) {
208 maxEccPow = 15;
209 } else {
210 maxEccPow = 20;
211 }
212
213
214 maxHansen = maxEccPow / 2;
215
216 }
217
218
219 public void initializeStep(final AuxiliaryElements aux) throws OrekitException {
220
221
222 a = aux.getSma();
223 k = aux.getK();
224 h = aux.getH();
225 q = aux.getQ();
226 p = aux.getP();
227 lm = aux.getLM();
228
229
230 ecc = aux.getEcc();
231
232
233 I = aux.getRetrogradeFactor();
234
235
236 f = aux.getVectorF();
237 g = aux.getVectorG();
238
239
240 final Transform t = bodyFrame.getTransformTo(aux.getFrame(), aux.getDate());
241 final Vector3D xB = t.transformVector(Vector3D.PLUS_I);
242 final Vector3D yB = t.transformVector(Vector3D.PLUS_J);
243 theta = FastMath.atan2(-f.dotProduct(yB) + I * g.dotProduct(xB),
244 f.dotProduct(xB) + I * g.dotProduct(yB));
245
246
247 alpha = aux.getAlpha();
248 beta = aux.getBeta();
249 gamma = aux.getGamma();
250
251
252
253 final double A = aux.getA();
254
255 final double B = aux.getB();
256
257 final double C = aux.getC();
258
259
260 ax2oA = 2. * a / A;
261
262 BoA = B / A;
263
264 ooAB = 1. / (A * B);
265
266 Co2AB = C * ooAB / 2.;
267
268 BoABpo = BoA / (1. + B);
269
270 moa = provider.getMu() / a;
271
272 roa = provider.getAe() / a;
273
274 }
275
276
277 public double[] getMeanElementRate(final SpacecraftState spacecraftState) throws OrekitException {
278
279
280 final double[] dU = computeUDerivatives(spacecraftState.getDate());
281 final double dUda = dU[0];
282 final double dUdh = dU[1];
283 final double dUdk = dU[2];
284 final double dUdl = dU[3];
285 final double dUdAl = dU[4];
286 final double dUdBe = dU[5];
287 final double dUdGa = dU[6];
288
289
290 final double UAlphaGamma = alpha * dUdGa - gamma * dUdAl;
291 final double UAlphaBeta = alpha * dUdBe - beta * dUdAl;
292 final double UBetaGamma = beta * dUdGa - gamma * dUdBe;
293 final double Uhk = h * dUdk - k * dUdh;
294 final double pUagmIqUbgoAB = (p * UAlphaGamma - I * q * UBetaGamma) * ooAB;
295 final double UhkmUabmdUdl = Uhk - UAlphaBeta - dUdl;
296
297 final double da = ax2oA * dUdl;
298 final double dh = BoA * dUdk + k * pUagmIqUbgoAB - h * BoABpo * dUdl;
299 final double dk = -(BoA * dUdh + h * pUagmIqUbgoAB + k * BoABpo * dUdl);
300 final double dp = Co2AB * (p * UhkmUabmdUdl - UBetaGamma);
301 final double dq = Co2AB * (q * UhkmUabmdUdl - I * UAlphaGamma);
302 final double dM = -ax2oA * dUda + BoABpo * (h * dUdh + k * dUdk) + pUagmIqUbgoAB;
303
304 return new double[] {da, dk, dh, dq, dp, dM};
305 }
306
307
308 public double[] getShortPeriodicVariations(final AbsoluteDate date,
309 final double[] meanElements)
310 throws OrekitException {
311
312 return new double[] {0., 0., 0., 0., 0., 0.};
313 }
314
315
316 public EventDetector[] getEventsDetectors() {
317 return null;
318 }
319
320
321
322 private void getResonantTerms() {
323
324
325 final double tolerance = 1. / FastMath.max(MIN_PERIOD_IN_SAT_REV,
326 MIN_PERIOD_IN_SECONDS / orbitPeriod);
327
328
329 final int maxOrder = provider.getMaxOrder();
330 for (int m = 1; m <= maxOrder; m++) {
331 final double resonance = ratio * m;
332 final int j = (int) FastMath.round(resonance);
333 if (j > 0 && FastMath.abs(resonance - j) <= tolerance) {
334
335 resOrders.add(m);
336 }
337 }
338 }
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357 private double[] computeUDerivatives(final AbsoluteDate date) throws OrekitException {
358
359
360 double dUda = 0.;
361 double dUdh = 0.;
362 double dUdk = 0.;
363 double dUdl = 0.;
364 double dUdAl = 0.;
365 double dUdBe = 0.;
366 double dUdGa = 0.;
367
368
369 if (!resOrders.isEmpty()) {
370
371 final GHmsjPolynomials ghMSJ = new GHmsjPolynomials(k, h, alpha, beta, I);
372
373
374 final GammaMnsFunction gammaMNS = new GammaMnsFunction(fact, gamma, I);
375
376
377 final HansenTesseral hansen = new HansenTesseral(ecc, maxHansen);
378
379
380 final double[] roaPow = new double[maxDegree + 1];
381 roaPow[0] = 1.;
382 for (int i = 1; i <= maxDegree; i++) {
383 roaPow[i] = roa * roaPow[i - 1];
384 }
385
386
387 for (int m : resOrders) {
388
389
390 final int j = FastMath.max(1, (int) FastMath.round(ratio * m));
391
392
393 final double jlMmt = j * lm - m * theta;
394 final double sinPhi = FastMath.sin(jlMmt);
395 final double cosPhi = FastMath.cos(jlMmt);
396
397
398 double dUdaCos = 0.;
399 double dUdaSin = 0.;
400 double dUdhCos = 0.;
401 double dUdhSin = 0.;
402 double dUdkCos = 0.;
403 double dUdkSin = 0.;
404 double dUdlCos = 0.;
405 double dUdlSin = 0.;
406 double dUdAlCos = 0.;
407 double dUdAlSin = 0.;
408 double dUdBeCos = 0.;
409 double dUdBeSin = 0.;
410 double dUdGaCos = 0.;
411 double dUdGaSin = 0.;
412
413
414 final int sMin = FastMath.min(maxEccPow - j, maxDegree);
415 final int sMax = FastMath.min(maxEccPow + j, maxDegree);
416 for (int s = 0; s <= sMax; s++) {
417
418
419 final double[][] nSumSpos = computeNSum(date, j, m, s,
420 roaPow, ghMSJ, gammaMNS, hansen);
421 dUdaCos += nSumSpos[0][0];
422 dUdaSin += nSumSpos[0][1];
423 dUdhCos += nSumSpos[1][0];
424 dUdhSin += nSumSpos[1][1];
425 dUdkCos += nSumSpos[2][0];
426 dUdkSin += nSumSpos[2][1];
427 dUdlCos += nSumSpos[3][0];
428 dUdlSin += nSumSpos[3][1];
429 dUdAlCos += nSumSpos[4][0];
430 dUdAlSin += nSumSpos[4][1];
431 dUdBeCos += nSumSpos[5][0];
432 dUdBeSin += nSumSpos[5][1];
433 dUdGaCos += nSumSpos[6][0];
434 dUdGaSin += nSumSpos[6][1];
435
436
437 if (s > 0 && s <= sMin) {
438 final double[][] nSumSneg = computeNSum(date, j, m, -s,
439 roaPow, ghMSJ, gammaMNS, hansen);
440 dUdaCos += nSumSneg[0][0];
441 dUdaSin += nSumSneg[0][1];
442 dUdhCos += nSumSneg[1][0];
443 dUdhSin += nSumSneg[1][1];
444 dUdkCos += nSumSneg[2][0];
445 dUdkSin += nSumSneg[2][1];
446 dUdlCos += nSumSneg[3][0];
447 dUdlSin += nSumSneg[3][1];
448 dUdAlCos += nSumSneg[4][0];
449 dUdAlSin += nSumSneg[4][1];
450 dUdBeCos += nSumSneg[5][0];
451 dUdBeSin += nSumSneg[5][1];
452 dUdGaCos += nSumSneg[6][0];
453 dUdGaSin += nSumSneg[6][1];
454 }
455 }
456
457
458 dUda += cosPhi * dUdaCos + sinPhi * dUdaSin;
459 dUdh += cosPhi * dUdhCos + sinPhi * dUdhSin;
460 dUdk += cosPhi * dUdkCos + sinPhi * dUdkSin;
461 dUdl += cosPhi * dUdlCos + sinPhi * dUdlSin;
462 dUdAl += cosPhi * dUdAlCos + sinPhi * dUdAlSin;
463 dUdBe += cosPhi * dUdBeCos + sinPhi * dUdBeSin;
464 dUdGa += cosPhi * dUdGaCos + sinPhi * dUdGaSin;
465 }
466
467 dUda *= -moa / a;
468 dUdh *= moa;
469 dUdk *= moa;
470 dUdl *= moa;
471 dUdAl *= moa;
472 dUdBe *= moa;
473 dUdGa *= moa;
474 }
475
476 return new double[] {dUda, dUdh, dUdk, dUdl, dUdAl, dUdBe, dUdGa};
477 }
478
479
480
481
482
483
484
485
486
487
488
489
490
491 private double[][] computeNSum(final AbsoluteDate date,
492 final int j, final int m, final int s,
493 final double[] roaPow,
494 final GHmsjPolynomials ghMSJ,
495 final GammaMnsFunction gammaMNS,
496 final HansenTesseral hansen) throws OrekitException {
497
498
499 final UnnormalizedSphericalHarmonics harmonics = provider.onDate(date);
500
501
502 double dUdaCos = 0.;
503 double dUdaSin = 0.;
504 double dUdhCos = 0.;
505 double dUdhSin = 0.;
506 double dUdkCos = 0.;
507 double dUdkSin = 0.;
508 double dUdlCos = 0.;
509 double dUdlSin = 0.;
510 double dUdAlCos = 0.;
511 double dUdAlSin = 0.;
512 double dUdBeCos = 0.;
513 double dUdBeSin = 0.;
514 double dUdGaCos = 0.;
515 double dUdGaSin = 0.;
516
517
518 final int Im = I > 0 ? 1 : (m % 2 == 0 ? 1 : -1);
519
520
521 final int v = FastMath.abs(m - s);
522 final int w = FastMath.abs(m + s);
523
524
525 final int nmin = FastMath.max(FastMath.max(2, m), FastMath.abs(s));
526
527 final int nmax = nmin + 3;
528
529
530 for (int n = nmin; n <= maxDegree; n++) {
531
532
533 if (n <= nmax) {
534
535 hansen.valueFromNewcomb(j, -n - 1, s);
536 hansen.derivFromNewcomb(j, -n - 1, s);
537 } else {
538
539 hansen.valueFromRecurrence(j, -n - 1, s);
540 hansen.derivFromRecurrence(j, -n - 1, s);
541 }
542
543
544 if ((n - s) % 2 == 0) {
545
546
547 final double fns = fact[n + FastMath.abs(s)];
548 final double vMNS = CoefficientsFactory.getVmns(m, n, s, fns, fact[n - m]);
549
550
551 final double gaMNS = gammaMNS.getValue(m, n, s);
552 final double dGaMNS = gammaMNS.getDerivative(m, n, s);
553
554
555 final double kJNS = hansen.getValue(j, -n - 1, s);
556 final double dkJNS = hansen.getDeriv(j, -n - 1, s);
557
558
559 final double gMSJ = ghMSJ.getGmsj(m, s, j);
560 final double hMSJ = ghMSJ.getHmsj(m, s, j);
561 final double dGdh = ghMSJ.getdGmsdh(m, s, j);
562 final double dGdk = ghMSJ.getdGmsdk(m, s, j);
563 final double dGdA = ghMSJ.getdGmsdAlpha(m, s, j);
564 final double dGdB = ghMSJ.getdGmsdBeta(m, s, j);
565 final double dHdh = ghMSJ.getdHmsdh(m, s, j);
566 final double dHdk = ghMSJ.getdHmsdk(m, s, j);
567 final double dHdA = ghMSJ.getdHmsdAlpha(m, s, j);
568 final double dHdB = ghMSJ.getdHmsdBeta(m, s, j);
569
570
571 final int l = FastMath.min(n - m, n - FastMath.abs(s));
572
573 final DerivativeStructure jacobi =
574 JacobiPolynomials.getValue(l, v , w, new DerivativeStructure(1, 1, 0, gamma));
575
576
577 final double cnm = harmonics.getUnnormalizedCnm(n, m);
578 final double snm = harmonics.getUnnormalizedSnm(n, m);
579
580
581 final double cf_0 = roaPow[n] * Im * vMNS;
582 final double cf_1 = cf_0 * gaMNS * jacobi.getValue();
583 final double cf_2 = cf_1 * kJNS;
584 final double gcPhs = gMSJ * cnm + hMSJ * snm;
585 final double gsMhc = gMSJ * snm - hMSJ * cnm;
586 final double dKgcPhsx2 = 2. * dkJNS * gcPhs;
587 final double dKgsMhcx2 = 2. * dkJNS * gsMhc;
588 final double dUdaCoef = (n + 1) * cf_2;
589 final double dUdlCoef = j * cf_2;
590 final double dUdGaCoef = cf_0 * kJNS * (jacobi.getValue() * dGaMNS + gaMNS * jacobi.getPartialDerivative(1));
591
592
593 dUdaCos += dUdaCoef * gcPhs;
594 dUdaSin += dUdaCoef * gsMhc;
595
596
597 dUdhCos += cf_1 * (kJNS * (cnm * dGdh + snm * dHdh) + h * dKgcPhsx2);
598 dUdhSin += cf_1 * (kJNS * (snm * dGdh - cnm * dHdh) + h * dKgsMhcx2);
599
600
601 dUdkCos += cf_1 * (kJNS * (cnm * dGdk + snm * dHdk) + k * dKgcPhsx2);
602 dUdkSin += cf_1 * (kJNS * (snm * dGdk - cnm * dHdk) + k * dKgsMhcx2);
603
604
605 dUdlCos += dUdlCoef * gsMhc;
606 dUdlSin += -dUdlCoef * gcPhs;
607
608
609 dUdAlCos += cf_2 * (dGdA * cnm + dHdA * snm);
610 dUdAlSin += cf_2 * (dGdA * snm - dHdA * cnm);
611
612
613 dUdBeCos += cf_2 * (dGdB * cnm + dHdB * snm);
614 dUdBeSin += cf_2 * (dGdB * snm - dHdB * cnm);
615
616
617 dUdGaCos += dUdGaCoef * gcPhs;
618 dUdGaSin += dUdGaCoef * gsMhc;
619 }
620 }
621
622 return new double[][] {{dUdaCos, dUdaSin},
623 {dUdhCos, dUdhSin},
624 {dUdkCos, dUdkSin},
625 {dUdlCos, dUdlSin},
626 {dUdAlCos, dUdAlSin},
627 {dUdBeCos, dUdBeSin},
628 {dUdGaCos, dUdGaSin}};
629 }
630
631
632
633
634
635
636
637 private static class HansenTesseral {
638
639
640 private TreeMap<MNSKey, Double> values;
641
642
643 private TreeMap<MNSKey, Double> derivatives;
644
645
646 private final double e2;
647
648
649 private final double ome2;
650
651
652 private final double chi;
653
654
655 private final double chi2;
656
657
658 private final double dchi;
659
660
661 private final double dchi2;
662
663
664
665
666 private final int maxNewcomb;
667
668
669
670
671
672 public HansenTesseral(final double ecc, final int maxHansen) {
673 this.values = new TreeMap<CoefficientsFactory.MNSKey, Double>();
674 this.derivatives = new TreeMap<CoefficientsFactory.MNSKey, Double>();
675 this.maxNewcomb = maxHansen;
676 this.e2 = ecc * ecc;
677 this.ome2 = 1. - e2;
678 this.chi = 1. / FastMath.sqrt(ome2);
679 this.chi2 = chi * chi;
680 this.dchi = 0.5 * chi * chi2;
681 this.dchi2 = chi2 * chi2;
682 }
683
684
685
686
687
688
689
690 public double getValue(final int j, final int mnm1, final int s) {
691 return values.get(new MNSKey(j, mnm1, s));
692 }
693
694
695
696
697
698
699
700 public double getDeriv(final int j, final int mnm1, final int s) {
701 return derivatives.get(new MNSKey(j, mnm1, s));
702 }
703
704
705
706
707
708
709
710
711
712 public void valueFromNewcomb(final int j, final int mnm1, final int s) {
713
714 final int aHT = FastMath.max(j - s, 0);
715 final int bHT = FastMath.max(s - j, 0);
716
717 double sum = NewcombOperators.getValue(maxNewcomb + aHT, maxNewcomb + bHT, mnm1, s);
718 for (int alphaHT = maxNewcomb - 1; alphaHT >= 0; alphaHT--) {
719 sum *= e2;
720 sum += NewcombOperators.getValue(alphaHT + aHT, alphaHT + bHT, mnm1, s);
721 }
722
723 final double value = FastMath.pow(chi2, -mnm1 - 1) * sum / chi;
724
725 values.put(new MNSKey(j, mnm1, s), value);
726 }
727
728
729
730
731
732
733
734
735
736 public void derivFromNewcomb(final int j, final int mnm1, final int s) {
737
738 final int aHT = FastMath.max(j - s, 0);
739 final int bHT = FastMath.max(s - j, 0);
740
741 double sum = maxNewcomb * NewcombOperators.getValue(maxNewcomb + aHT, maxNewcomb + bHT, mnm1, s);
742 for (int alphaHT = maxNewcomb - 1; alphaHT >= 1; alphaHT--) {
743 sum *= e2;
744 sum += alphaHT * NewcombOperators.getValue(alphaHT + aHT, alphaHT + bHT, mnm1, s);
745 }
746
747 final MNSKey key = new MNSKey(j, mnm1, s);
748 final double coef = -(mnm1 + 1.5);
749 final double Kjns = values.get(key);
750 final double derivative = coef * chi2 * Kjns + FastMath.pow(chi2, -mnm1 - 1) * sum / chi;
751
752 derivatives.put(new MNSKey(j, mnm1, s), derivative);
753 }
754
755
756
757
758
759
760
761 public void valueFromRecurrence(final int j, final int mnm1, final int s) {
762 final int n = -mnm1 - 1;
763 final double kmn = values.get(new MNSKey(j, -n, s));
764 final double kmnp1 = values.get(new MNSKey(j, -n + 1, s));
765 final double kmnp3 = values.get(new MNSKey(j, -n + 3, s));
766 final double den = (3 - n) * (1 - n + s) * (1 - n - s);
767 final double ck = chi2 / den;
768 final double ckmn = (3 - n) * (1 - n) * (3 - 2 * n);
769 final double ckmnp1 = (2 - n) * ((3 - n) * (1 - n) + (2 * j * s) / chi);
770 final double ckmnp3 = j * j * (1 - n);
771 final double value = ck * (ckmn * kmn - ckmnp1 * kmnp1 + ckmnp3 * kmnp3);
772
773 values.put(new MNSKey(j, mnm1, s), value);
774 }
775
776
777
778
779
780
781
782 public void derivFromRecurrence(final int j, final int mnm1, final int s) {
783 final int n = -mnm1 - 1;
784 final MNSKey keymn = new MNSKey(j, -n, s);
785 final MNSKey keymnp1 = new MNSKey(j, -n + 1, s);
786 final MNSKey keymnp3 = new MNSKey(j, -n + 3, s);
787 final double kmn = values.get(keymn);
788 final double kmnp1 = values.get(keymnp1);
789 final double kmnp3 = values.get(keymnp3);
790 final double dkmn = derivatives.get(keymn);
791 final double dkmnp1 = derivatives.get(keymnp1);
792 final double dkmnp3 = derivatives.get(keymnp3);
793 final double den = (3 - n) * (1 - n + s) * (1 - n - s);
794 final double cdkmn = (3 - n) * (1 - n) * (3 - 2 * n);
795 final double c0dkmnp1 = (n - 2) * (3 - n) * (1 - n);
796 final double c1dkmnp1 = (n - 2) * (2 * j * s);
797 final double cdkmnp3 = j * j * (1 - n);
798 final double deriv = chi2 * (cdkmn * dkmn + c0dkmnp1 * dkmnp1 + cdkmnp3 * dkmnp3) +
799 chi * c1dkmnp1 * dkmnp1 +
800 dchi2 * (cdkmn * kmn + c0dkmnp1 * kmnp1 + cdkmnp3 * kmnp3) +
801 dchi * c1dkmnp1 * kmnp1;
802
803 derivatives.put(new MNSKey(j, mnm1, s), deriv / den);
804 }
805 }
806 }