FieldNeQuickParameters.java

  1. /* Copyright 2002-2025 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.models.earth.ionosphere.nequick;

  18. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.util.FastMath;
  20. import org.hipparchus.util.FieldSinCos;
  21. import org.hipparchus.util.MathArrays;
  22. import org.orekit.time.DateComponents;
  23. import org.orekit.time.DateTimeComponents;
  24. import org.orekit.time.TimeComponents;

  25. /**
  26.  * This class performs the computation of the parameters used by the NeQuick model.
  27.  *
  28.  * @author Bryan Cazabonne
  29.  *
  30.  * @see "European Union (2016). European GNSS (Galileo) Open Service-Ionospheric Correction
  31.  *       Algorithm for Galileo Single Frequency Users. 1.2."
  32.  * @see <a href="https://www.itu.int/rec/R-REC-P.531/en">ITU-R P.531</a>
  33.  *
  34.  * @since 10.1
  35.  * @param <T> type of the field elements
  36.  */
  37. class FieldNeQuickParameters <T extends CalculusFieldElement<T>> {

  38.     /** Solar zenith angle at day night transition, degrees. */
  39.     private static final double X0 = 86.23292796211615;

  40.     /** Current date time components.
  41.      * @since 13.0
  42.      */
  43.     private final DateTimeComponents dateTime;

  44.     /** Effective sunspot number.
  45.      * @since 13.0
  46.      */
  47.     private final T azr;

  48.     /** F2 layer critical frequency.
  49.      * @since 13.0
  50.      */
  51.     private final T foF2;

  52.     /** F2 layer maximum density. */
  53.     private final T nmF2;

  54.     /** F2 layer maximum density height [km]. */
  55.     private final T hmF2;

  56.     /** F1 layer maximum density height [km]. */
  57.     private final T hmF1;

  58.     /** E layer maximum density height [km]. */
  59.     private final T hmE;

  60.     /** F2 layer bottom thickness parameter [km]. */
  61.     private final T b2Bot;

  62.     /** F1 layer top thickness parameter [km]. */
  63.     private final T b1Top;

  64.     /** F1 layer bottom thickness parameter [km]. */
  65.     private final T b1Bot;

  66.     /** E layer top thickness parameter [km]. */
  67.     private final T beTop;

  68.     /** E layer bottom thickness parameter [km]. */
  69.     private final T beBot;

  70.     /** Layer amplitudes. */
  71.     private final T[] amplitudes;

  72.     /**
  73.      * Build a new instance.
  74.      * @param dateTime current date time components
  75.      * @param flattenF2 F2 coefficients used by the F2 layer (flatten array)
  76.      * @param flattenFm3 Fm3 coefficients used by the F2 layer (flatten array)
  77.      * @param latitude latitude of a point along the integration path, in radians
  78.      * @param longitude longitude of a point along the integration path, in radians
  79.      * @param az effective ionisation level
  80.      * @param modip modip
  81.      */
  82.     FieldNeQuickParameters(final DateTimeComponents dateTime, final double[] flattenF2,
  83.                            final double[] flattenFm3, final T latitude, final T longitude, final T az,
  84.                            final T modip) {
  85.         this(new FieldFourierTimeSeries<>(dateTime, az, flattenF2, flattenFm3),
  86.              latitude, longitude, modip);
  87.     }

  88.     /**
  89.      * Build a new instance.
  90.      * @param fourierTimeSeries Fourier time series for foF2 and M(3000)F2 layer
  91.      * @param latitude latitude of a point along the integration path, in radians
  92.      * @param longitude longitude of a point along the integration path, in radians
  93.      * @param modip modip
  94.      */
  95.     FieldNeQuickParameters(final FieldFourierTimeSeries<T> fourierTimeSeries,
  96.                            final T latitude, final T longitude, final T modip) {

  97.         // Zero
  98.         final T zero = latitude.getField().getZero();

  99.         this.dateTime = fourierTimeSeries.getDateTime();
  100.         this.azr      = fourierTimeSeries.getAzr();

  101.         // Date and Time components
  102.         final DateComponents date = dateTime.getDate();
  103.         final TimeComponents time = dateTime.getTime();
  104.         // Hours
  105.         final double hours  = time.getSecondsInUTCDay() / 3600.0;
  106.         // Effective solar zenith angle in radians
  107.         final T xeff = computeEffectiveSolarAngle(date.getMonth(), hours, latitude, longitude);

  108.         // E layer maximum density height in km (Eq. 78)
  109.         this.hmE = zero.newInstance(120.0);
  110.         // E layer critical frequency in MHz
  111.         final T foE = computefoE(date.getMonth(), fourierTimeSeries.getAz(), xeff, latitude);
  112.         // E layer maximum density in 10^11 m-3 (Eq. 36)
  113.         final T nmE = foE.multiply(foE).multiply(0.124);

  114.         // F2 layer critical frequency in MHz
  115.         final T[] scL = FieldFourierTimeSeries.sinCos(longitude, 8);
  116.         this.foF2 = computefoF2(modip, fourierTimeSeries.getCf2Reference(), latitude, scL);
  117.         // Maximum Usable Frequency factor
  118.         final T mF2  = computeMF2(modip, fourierTimeSeries.getCm3Reference(), latitude, scL);
  119.         // F2 layer maximum density in 10^11 m-3
  120.         this.nmF2 = foF2.multiply(foF2).multiply(0.124);
  121.         // F2 layer maximum density height in km
  122.         this.hmF2 = computehmF2(foE, mF2);

  123.         // F1 layer critical frequency in MHz
  124.         final T foF1 = computefoF1(foE);
  125.         // F1 layer maximum density in 10^11 m-3
  126.         final T nmF1;
  127.         if (foF1.getReal() <= 0.0 && foE.getReal() > 2.0) {
  128.             final T foEpopf = foE.add(0.5);
  129.             nmF1 = foEpopf.multiply(foEpopf).multiply(0.124);
  130.         } else {
  131.             nmF1 = foF1.multiply(foF1).multiply(0.124);
  132.         }
  133.         // F1 layer maximum density height in km
  134.         this.hmF1 = hmF2.add(hmE).multiply(0.5);

  135.         // Thickness parameters (Eq. 85 to 89)
  136.         final T a = clipExp(FastMath.log(foF2.multiply(foF2)).multiply(0.857).add(FastMath.log(mF2).multiply(2.02)).add(-3.467)).multiply(0.01);
  137.         this.b2Bot = nmF2.divide(a).multiply(0.385);
  138.         this.b1Top = hmF2.subtract(hmF1).multiply(0.3);
  139.         this.b1Bot = hmF1.subtract(hmE).multiply(0.5);
  140.         this.beTop = FastMath.max(b1Bot, zero.newInstance(7.0));
  141.         this.beBot = zero.newInstance(5.0);

  142.         // Layer amplitude coefficients
  143.         this.amplitudes = computeLayerAmplitudes(nmE, nmF1, foF1);

  144.     }

  145.     /**
  146.      * Get current date time components.
  147.      * @return current date time components
  148.      * @since 13.0
  149.      */
  150.     public DateTimeComponents getDateTime() {
  151.         return dateTime;
  152.     }

  153.     /**
  154.      * Get effective sunspot number.
  155.      * @return effective sunspot number
  156.      * @since 13.0
  157.      */
  158.     public T getAzr() {
  159.         return azr;
  160.     }

  161.     /**
  162.      * Get F2 layer critical frequency.
  163.      * @return F2 layer critical frequency
  164.      * @since 13.0
  165.      */
  166.     public T getFoF2() {
  167.         return foF2;
  168.     }

  169.     /**
  170.      * Get the F2 layer maximum density.
  171.      * @return nmF2
  172.      */
  173.     public T getNmF2() {
  174.         return nmF2;
  175.     }

  176.     /**
  177.      * Get the F2 layer maximum density height.
  178.      * @return hmF2 in km
  179.      */
  180.     public T getHmF2() {
  181.         return hmF2;
  182.     }

  183.     /**
  184.      * Get the F1 layer maximum density height.
  185.      * @return hmF1 in km
  186.      */
  187.     public T getHmF1() {
  188.         return hmF1;
  189.     }

  190.     /**
  191.      * Get the E layer maximum density height.
  192.      * @return hmE in km
  193.      */
  194.     public T getHmE() {
  195.         return hmE;
  196.     }

  197.     /**
  198.      * Get the F2 layer thickness parameter (bottom).
  199.      * @return B2Bot in km
  200.      */
  201.     public T getB2Bot() {
  202.         return b2Bot;
  203.     }

  204.     /**
  205.      * Get the F1 layer thickness parameter.
  206.      * @param h current height (km)
  207.      * @return B1 in km
  208.      * @since 13.0
  209.      */
  210.     public T getBF1(final T h) {
  211.         // Eq. 110
  212.         return (h.getReal() > hmF1.getReal()) ? b1Top : b1Bot;
  213.     }

  214.     /**
  215.      * Get the E layer thickness parameter.
  216.      * @param h current height (km)
  217.      * @return Be in km
  218.      * @since 13.0
  219.      */
  220.     public T getBE(final T h) {
  221.         // Eq. 109
  222.         return (h.getReal() > hmE.getReal()) ? beTop : beBot;
  223.     }

  224.     /**
  225.      * Get the F2, F1 and E layer amplitudes.
  226.      * <p>
  227.      * The resulting element is an array having the following form:
  228.      * <ul>
  229.      * <li>double[0] = A1 → F2 layer amplitude
  230.      * <li>double[1] = A2 → F1 layer amplitude
  231.      * <li>double[2] = A3 → E  layer amplitude
  232.      * </ul>
  233.      * @return layer amplitudes
  234.      */
  235.     public T[] getLayerAmplitudes() {
  236.         return amplitudes.clone();
  237.     }

  238.     /**
  239.      * This method computes the effective solar zenith angle.
  240.      * <p>
  241.      * The effective solar zenith angle is compute as a function of the
  242.      * solar zenith angle and the solar zenith angle at day night transition.
  243.      * </p>
  244.      * @param month current month of the year
  245.      * @param hours universal time (hours)
  246.      * @param latitude in radians
  247.      * @param longitude in radians
  248.      * @return the effective solar zenith angle, radians
  249.      */
  250.     private T computeEffectiveSolarAngle(final int month,
  251.                                          final double hours,
  252.                                          final T latitude,
  253.                                          final T longitude) {
  254.         // Zero
  255.         final T zero = latitude.getField().getZero();
  256.         // Local time (Eq.4)
  257.         final T lt = longitude.divide(FastMath.toRadians(15.0)).add(hours);
  258.         // Day of year at the middle of the month (Eq. 20)
  259.         final double dy = 30.5 * month - 15.0;
  260.         // Time (Eq. 21)
  261.         final double t = dy + (18 - hours) / 24;
  262.         // Arguments am and al (Eq. 22 and 23)
  263.         final double am = FastMath.toRadians(0.9856 * t - 3.289);
  264.         final double al = am + FastMath.toRadians(1.916 * FastMath.sin(am) + 0.020 * FastMath.sin(2.0 * am) + 282.634);
  265.         // Sine and cosine of solar declination (Eq. 24 and 25)
  266.         final double sDec = 0.39782 * FastMath.sin(al);
  267.         final double cDec = FastMath.sqrt(1. - sDec * sDec);
  268.         // Solar zenith angle, deg (Eq. 26 and 27)
  269.         final FieldSinCos<T> scLat   = FastMath.sinCos(latitude);
  270.         final T coef    = lt.negate().add(12.0).multiply(FastMath.PI / 12);
  271.         final T cZenith = scLat.sin().multiply(sDec).add(scLat.cos().multiply(cDec).multiply(FastMath.cos(coef)));
  272.         final T angle   = FastMath.atan2(FastMath.sqrt(cZenith.multiply(cZenith).negate().add(1.0)), cZenith);
  273.         final T x       = FastMath.toDegrees(angle);
  274.         // Effective solar zenith angle (Eq. 28)
  275.         final T xeff = join(clipExp(x.multiply(0.2).negate().add(20.0)).multiply(0.24).negate().add(90.0), x,
  276.                 zero.newInstance(12.0), x.subtract(X0));
  277.         return FastMath.toRadians(xeff);
  278.     }

  279.     /**
  280.      * This method computes the E layer critical frequency at a given location.
  281.      * @param month current month
  282.      * @param az ffective ionisation level
  283.      * @param xeff effective solar zenith angle in radians
  284.      * @param latitude latitude in radians
  285.      * @return the E layer critical frequency at a given location in MHz
  286.      */
  287.     private T computefoE(final int month, final T az,
  288.                          final T xeff, final T latitude) {
  289.         // The latitude has to be converted in degrees
  290.         final T lat = FastMath.toDegrees(latitude);
  291.         // Square root of the effective ionisation level
  292.         final T sqAz = FastMath.sqrt(az);
  293.         // seas parameter (Eq. 30 to 32)
  294.         final int seas;
  295.         if (month == 1 || month == 2 || month == 11 || month == 12) {
  296.             seas = -1;
  297.         } else if (month == 3 || month == 4 || month == 9 || month == 10) {
  298.             seas = 0;
  299.         } else {
  300.             seas = 1;
  301.         }
  302.         // Latitudinal dependence (Eq. 33 and 34)
  303.         final T ee = clipExp(lat.multiply(0.3));
  304.         final T seasp = ee.subtract(1.0).divide(ee.add(1.0)).multiply(seas);
  305.         // Critical frequency (Eq. 35)
  306.         final T coef = seasp.multiply(0.019).negate().add(1.112);
  307.         return FastMath.sqrt(coef .multiply(coef).multiply(sqAz).multiply(FastMath.cos(xeff).pow(0.6)).add(0.49));

  308.     }

  309.     /**
  310.      * Computes the F2 layer height of maximum electron density.
  311.      * @param foE E layer layer critical frequency in MHz
  312.      * @param mF2 maximum usable frequency factor
  313.      * @return hmF2 in km
  314.      */
  315.     private T computehmF2(final T foE, final T mF2) {
  316.         // Zero
  317.         final T zero = foE.getField().getZero();
  318.         // Ratio
  319.         final T fo = foF2.divide(foE);
  320.         final T ratio = join(fo, zero.newInstance(1.75), zero.newInstance(20.0), fo.subtract(1.75));

  321.         // deltaM parameter
  322.         T deltaM = zero.subtract(0.012);
  323.         if (foE.getReal() >= 1e-30) {
  324.             deltaM = deltaM.add(ratio.subtract(1.215).divide(0.253).reciprocal());
  325.         }

  326.         // hmF2 Eq. 80
  327.         final T mF2Sq = mF2.square();
  328.         final T temp  = FastMath.sqrt(mF2Sq.multiply(0.0196).add(1.0).divide(mF2Sq.multiply(1.2967).subtract(1.0)));
  329.         return mF2.multiply(1490.0).multiply(temp).divide(mF2.add(deltaM)).subtract(176.0);

  330.     }

  331.     /**
  332.      * This method computes the F2 layer critical frequency.
  333.      * @param modip modified DIP latitude, in degrees
  334.      * @param cf2 Fourier time series for foF2
  335.      * @param latitude latitude in radians
  336.      * @param scL sines/cosines array of longitude argument
  337.      * @return the F2 layer critical frequency, MHz
  338.      */
  339.     private T computefoF2(final T modip, final T[] cf2,
  340.                           final T latitude, final T[] scL) {

  341.         // Legendre grades (Eq. 63)
  342.         final int[] q = new int[] {
  343.             12, 12, 9, 5, 2, 1, 1, 1, 1
  344.         };

  345.         T frequency = cf2[0];

  346.         // ModipGrid coefficients Eq. 57
  347.         final T sinMODIP = FastMath.sin(FastMath.toRadians(modip));
  348.         final T[] m = MathArrays.buildArray(latitude.getField(), 12);
  349.         m[0] = latitude.getField().getOne();
  350.         for (int i = 1; i < q[0]; i++) {
  351.             m[i] = sinMODIP.multiply(m[i - 1]);
  352.             frequency = frequency.add(m[i].multiply(cf2[i]));
  353.         }

  354.         // latitude and longitude terms
  355.         int index = 12;
  356.         final T cosLat1 = FastMath.cos(latitude);
  357.         T cosLatI = cosLat1;
  358.         for (int i = 1; i < q.length; i++) {
  359.             final T c = cosLatI.multiply(scL[2 * i - 1]);
  360.             final T s = cosLatI.multiply(scL[2 * i - 2]);
  361.             for (int j = 0; j < q[i]; j++) {
  362.                 frequency = frequency.add(m[j].multiply(c).multiply(cf2[index++]));
  363.                 frequency = frequency.add(m[j].multiply(s).multiply(cf2[index++]));
  364.             }
  365.             cosLatI = cosLatI.multiply(cosLat1);
  366.         }

  367.         return frequency;

  368.     }

  369.     /**
  370.      * This method computes the Maximum Usable Frequency factor.
  371.      * @param modip modified DIP latitude, in degrees
  372.      * @param cm3 Fourier time series for M(3000)F2
  373.      * @param latitude latitude in radians
  374.      * @param scL sines/cosines array of longitude argument
  375.      * @return the Maximum Usable Frequency factor
  376.      */
  377.     private T computeMF2(final T modip, final T[] cm3,
  378.                          final T latitude, final T[] scL) {

  379.         // Legendre grades (Eq. 71)
  380.         final int[] r = new int[] {
  381.             7, 8, 6, 3, 2, 1, 1
  382.         };

  383.         T m3000 = cm3[0];

  384.         // ModipGrid coefficients Eq. 57
  385.         final T sinMODIP = FastMath.sin(FastMath.toRadians(modip));
  386.         final T[] m = MathArrays.buildArray(latitude.getField(), 12);
  387.         m[0] = latitude.getField().getOne();
  388.         for (int i = 1; i < 12; i++) {
  389.             m[i] = sinMODIP.multiply(m[i - 1]);
  390.             if (i < 7) {
  391.                 m3000 = m3000.add(m[i].multiply(cm3[i]));
  392.             }
  393.         }

  394.         // latitude and longitude terms
  395.         int index = 7;
  396.         final T cosLat1 = FastMath.cos(latitude);
  397.         T cosLatI = cosLat1;
  398.         for (int i = 1; i < r.length; i++) {
  399.             final T c = cosLatI.multiply(scL[2 * i - 1]);
  400.             final T s = cosLatI.multiply(scL[2 * i - 2]);
  401.             for (int j = 0; j < r[i]; j++) {
  402.                 m3000 = m3000.add(m[j].multiply(c).multiply(cm3[index++]));
  403.                 m3000 = m3000.add(m[j].multiply(s).multiply(cm3[index++]));
  404.             }
  405.             cosLatI = cosLatI.multiply(cosLat1); // Eq. 58
  406.         }

  407.         return m3000;

  408.     }

  409.     /**
  410.      * This method computes the F1 layer critical frequency.
  411.      * <p>
  412.      * This computation performs the algorithm exposed in Annex F
  413.      * of the reference document.
  414.      * </p>
  415.      * @param foE the E layer critical frequency, MHz
  416.      * @return the F1 layer critical frequency, MHz
  417.      */
  418.     private T computefoF1(final T foE) {
  419.         final T zero = foE.getField().getZero();
  420.         final T temp  = join(foE.multiply(1.4), zero, zero.newInstance(1000.0), foE.subtract(2.0));
  421.         final T temp2 = join(zero, temp, zero.newInstance(1000.0), foE.subtract(temp));
  422.         final T value = join(temp2, temp2.multiply(0.85), zero.newInstance(60.0), foF2.multiply(0.85).subtract(temp2));
  423.         if (value.getReal() < 1.0E-6) {
  424.             return zero;
  425.         } else {
  426.             return value;
  427.         }
  428.     }

  429.     /**
  430.      * This method allows the computation of the F2, F1 and E layer amplitudes.
  431.      * <p>
  432.      * The resulting element is an array having the following form:
  433.      * <ul>
  434.      * <li>double[0] = A1 → F2 layer amplitude
  435.      * <li>double[1] = A2 → F1 layer amplitude
  436.      * <li>double[2] = A3 → E  layer amplitude
  437.      * </ul>
  438.      * </p>
  439.      * @param nmE E layer maximum density in 10^11 m-3
  440.      * @param nmF1 F1 layer maximum density in 10^11 m-3
  441.      * @param foF1 F1 layer critical frequency in MHz
  442.      * @return a three components array containing the layer amplitudes
  443.      */
  444.     private T[] computeLayerAmplitudes(final T nmE, final T nmF1, final T foF1) {
  445.         // Zero
  446.         final T zero = nmE.getField().getZero();

  447.         // Initialize array
  448.         final T[] amplitude = MathArrays.buildArray(nmE.getField(), 3);

  449.         // F2 layer amplitude (Eq. 90)
  450.         final T a1 = nmF2.multiply(4.0);
  451.         amplitude[0] = a1;

  452.         // F1 and E layer amplitudes (Eq. 91 to 98)
  453.         if (foF1.getReal() < 0.5) {
  454.             amplitude[1] = zero;
  455.             amplitude[2] = nmE.subtract(epst(a1, hmF2, b2Bot, hmE)).multiply(4.0);
  456.         } else {
  457.             T a2a = zero;
  458.             T a3a = nmE.multiply(4.0);
  459.             for (int i = 0; i < 5; i++) {
  460.                 a2a = nmF1.subtract(epst(a1, hmF2, b2Bot, hmF1)).subtract(epst(a3a, hmE, beTop, hmF1)).multiply(4.0);
  461.                 a2a = join(a2a, nmF1.multiply(0.8), nmE.getField().getOne(), a2a.subtract(nmF1.multiply(0.8)));
  462.                 a3a = nmE.subtract(epst(a2a, hmF1, b1Bot, hmE)).subtract(epst(a1, hmF2, b2Bot, hmE)).multiply(4.0);
  463.             }
  464.             amplitude[1] = a2a;
  465.             amplitude[2] = join(a3a, zero.newInstance(0.05), zero.newInstance(60.0), a3a.subtract(0.005));
  466.         }

  467.         return amplitude;
  468.     }

  469.     /**
  470.      * A clipped exponential function.
  471.      * <p>
  472.      * This function, describe in section F.2.12.2 of the reference document, is
  473.      * recommanded for the computation of exponential values.
  474.      * </p>
  475.      * @param power power for exponential function
  476.      * @return clipped exponential value
  477.      */
  478.     private T clipExp(final T power) {
  479.         final T zero = power.getField().getZero();
  480.         if (power.getReal() > 80.0) {
  481.             return zero.newInstance(5.5406E34);
  482.         } else if (power.getReal() < -80) {
  483.             return zero.newInstance(1.8049E-35);
  484.         } else {
  485.             return FastMath.exp(power);
  486.         }
  487.     }

  488.     /**
  489.      * Allows smooth joining of functions f1 and f2
  490.      * (i.e. continuous first derivatives) at origin.
  491.      * <p>
  492.      * This function, describe in section F.2.12.1 of the reference document, is
  493.      * recommanded for computational efficiency.
  494.      * </p>
  495.      * @param dF1 first function
  496.      * @param dF2 second function
  497.      * @param dA width of transition region
  498.      * @param dX x value
  499.      * @return the computed value
  500.      */
  501.     T join(final T dF1, final T dF2, final T dA, final T dX) {
  502.         final T ee = clipExp(dA.multiply(dX));
  503.         return dF1.multiply(ee).add(dF2).divide(ee.add(1.0));
  504.     }

  505.     /**
  506.      * The Epstein function.
  507.      * <p>
  508.      * This function, describe in section 2.5.1 of the reference document, is used
  509.      * as a basis analytical function in NeQuick for the construction of the ionospheric layers.
  510.      * </p>
  511.      * @param x x parameter
  512.      * @param y y parameter
  513.      * @param z z parameter
  514.      * @param w w parameter
  515.      * @return value of the epstein function
  516.      */
  517.     private T epst(final T x, final T y,
  518.                    final T z, final T w) {
  519.         final T ex  = clipExp(w.subtract(y).divide(z));
  520.         final T opex = ex.add(1.0);
  521.         return x.multiply(ex).divide(opex.multiply(opex));

  522.     }

  523. }