# -*- coding: utf-8 -*-
# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Contributors:
# INITIAL AUTHORS - initial API and implementation
# and/or initial documentation
# :author: Sobieski, Agte, and Sandusky
# OTHER AUTHORS - MACROSCOPIC CHANGES
# :author: Damien Guenot
# :author: Francois Gallard
# From NASA/TM-1998-208715
# Bi-Level Integrated System Synthesis (BLISS)
# Sobieski, Agte, and Sandusky
"""
SSBJ Mission computation
************************
"""
from __future__ import division, unicode_literals
import logging
from math import pi
from numpy import zeros
LOGGER = logging.getLogger(__name__)
DEG_TO_RAD = pi / 180.0
[docs]class SobieskiMission(object):
"""Class defining mission analysis for Sobieski problem and related method to the
mission problem such as disciplines computation, constraints, reference optimum."""
DTYPE_COMPLEX = "complex128"
DTYPE_DOUBLE = "float64"
def __init__(self, sobieski_base):
"""Constructor."""
self.base = sobieski_base
self.dtype = self.base.dtype
self.math = self.base.math
[docs] @staticmethod
def compute_weight_ratio(y_14):
"""Computation of weight ratio of Breguet formula.
:param y_14: shared variables coming from blackbox_structure
- y_14[0]: total aircraft weight
- y_14[1]: fuel weight
:type y_14: numpy array
:returns: Wt / (Wt -Wf)
:rtype: numpy array
"""
return y_14[0] / (y_14[0] - y_14[1])
[docs] @staticmethod
def compute_dweightratio_dwt(y_14):
"""Computation of derivative of weight ratio wrt total weight.
:param y_14: shared variables coming from blackbox_structure:
- y_14[0]: total aircraft weight
- y_14[1]: fuel weight
:type y_14: numpy array
:returns: dweightratio_dtotalweight
:rtype: numpy array
"""
return -y_14[1] / ((y_14[0] - y_14[1]) * (y_14[0] - y_14[1]))
[docs] @staticmethod
def compute_dweightratio_dwf(y_14):
"""Computation of partial derivative of weight ratio wrt fuel weight.
:param y_14: shared variables coming from blackbox_structure:
- y_14[0]: total aircraft weight
- y_14[1]: fuel weight
:type y_14: numpy array
:returns: dweightratio_dfuelweight
:rtype: numpy array
"""
return y_14[0] / ((y_14[0] - y_14[1]) * (y_14[0] - y_14[1]))
[docs] def compute_dlnweightratio_dwt(self, y_14):
"""Computation of partial derivative of log of weight ratio wrt total weight.
:param y_14: shared variables coming from blackbox_structure:
- y_14[0]: total aircraft weight
- y_14[1]: fuel weight
:type y_14: numpy array
:returns: d(ln(weight ratio)/d(total weight)
:rtype: numpy array
"""
return self.compute_dweightratio_dwt(y_14) / self.compute_weight_ratio(y_14)
[docs] def compute_dlnweightratio_dwf(self, y_14):
"""Computation of partial derivative of log of weight ratio wrt fuel weight.
:param y_14: shared variables coming from blackbox_structure:
- y_14[0]: total aircraft weight
- y_14[1]: fuel weight
:type y_14: numpy array
:returns: d(ln(weight ratio)/d(fuel weight)
:rtype: numpy array
"""
return self.compute_dweightratio_dwf(y_14) / self.compute_weight_ratio(y_14)
[docs] def compute_range(self, x_shared, y_14, y_24, y_34):
"""Computation of range from Breguet formula.
:param x_shared: shared design variable vector:
- x_shared[0]: thickness/chord ratio
- x_shared[1]: altitude
- x_shared[2]: Mach
- x_shared[3]: aspect ratio
- x_shared[4]: wing sweep
- x_shared[5]: wing surface area
:type x_shared: numpy array
:param y_14: shared variables coming from blackbox_structure:
- y_14[0]: total aircraft weight
- y_14[1]: fuel weight
:type y_14: numpy array
:param y_24: shared variables coming from
blackbox_aerodynamics (lift/drag ratio)
:type y_24: numpy array
:param y_34: shared variables coming from
blackbox_propulsion (SFC)
:type y_34: numpy array
:returns: range value
:rtype: numpy array
"""
sqrt_theta = self.compute_sqrt_theta(x_shared)
return ((x_shared[2] * y_24[0]) * 661.0 * sqrt_theta / y_34[0]) * self.math.log(
y_14[0] / (y_14[0] - y_14[1])
)
[docs] def compute_drange_dtotalweight(self, x_shared, y_14, y_24, y_34, sqrt_theta):
"""Computation of range derivative wrt total weight.
:param x_shared: shared design variable vector
:type x_shared: numpy array
:param y_14: shared variables coming from blackbox_structure
:type y_14: numpy array
:param y_24: shared variables coming from blackbox_aerodynamics
:type y_24: numpy array
:param y_34: shared variables coming from blackbox_propulsion
:type y_34: numpy array
:param sqrt_theta: square root of air temperature
:type sqrt_theta: numpy array
:returns: d(range) / d(total weight)
:rtype: numpy array
"""
dlnweightratio_dtotalweight = self.compute_dlnweightratio_dwt(y_14)
return (
x_shared[2]
* y_24[0]
/ y_34[0]
* 661.0
* sqrt_theta
* dlnweightratio_dtotalweight
)
[docs] def compute_drange_dfuelweight(self, x_shared, y_14, y_24, y_34, sqrt_theta):
"""Computation of range derivative wrt fuel weight.
:param x_shared: shared design variable vector
:type x_shared: numpy array
:param y_14: shared variables coming from blackbox_structure
:type y_14: numpy array
:param y_24: shared variables coming from blackbox_aerodynamics
:type y_24: numpy array
:param y_34: shared variables coming from blackbox_propulsion
:type y_34: numpy array
:param sqrt_theta: square root of air temperature
:type sqrt_theta: numpy array
:returns: d(range) / d(fuel weight)
:rtype: numpy array
"""
dlnweightratio_dfuelweight = self.compute_dlnweightratio_dwf(y_14)
return (
x_shared[2]
* y_24[0]
/ y_34[0]
* 661.0
* sqrt_theta
* dlnweightratio_dfuelweight
)
[docs] def compute_dtheta_dh(self, x_shared):
"""Computation of air temperature and its derivative wrt altitude.
:param x_shared: shared design variable vector
:type x_shared: numpy array
:returns: square root of air temperature, dtheta_dh
:rtype: numpy array
"""
if x_shared[1] < 36089.0:
theta = 1 - 0.000006875 * x_shared[1]
dtheta_dh = -0.000006875
else:
theta = 0.7519
dtheta_dh = 0.0
return self.math.sqrt(theta), dtheta_dh
[docs] def compute_sqrt_theta(self, x_shared):
"""Computation of air temperature a.
:param x_shared: shared design variable vector
:type x_shared: numpy array
:returns: square root of air temperature
:rtype: numpy array
"""
if x_shared[1] < 36089.0:
theta = 1 - 0.000006875 * x_shared[1]
else:
theta = 0.7519
return self.math.sqrt(theta)
[docs] def blackbox_mission(self, x_shared, y_14, y_24, y_34):
"""THIS SECTION COMPUTES THE A/C RANGE from Breguet's law.
:param x_shared: shared design variable vector:
- x_shared[0]: thickness/chord ratio
- x_shared[1]: altitude
- x_shared[2]: Mach
- x_shared[3]: aspect ratio
- x_shared[4]: wing sweep
- x_shared[5]: wing surface area
:type x_shared: numpy array
:param y_14: shared variables coming from blackbox_structure:
- y_14[0]: total aircraft weight
- y_14[1]: fuel weight
:type y_14: numpy array
:param y_24: shared variables coming from
blackbox_aerodynamics (lift/drag ratio)
:type y_24: numpy array
:param y_34: shared variables coming from
blackbox_propulsion (SFC)
:type y_34: numpy array
:returns: y_4: range value
:rtype: numpy array
"""
y_4 = zeros(1, dtype=self.dtype)
y_4[0] = self.compute_range(x_shared, y_14, y_24, y_34)
return y_4
def __initialize_jacobian(self):
"""
Initialization of jacobian matrix
:returns: jacobian
:rtype: dict(dict(ndarray))
"""
jacobian = {"y_4": {}}
jacobian["y_4"]["x_shared"] = zeros((1, 6), dtype=self.dtype)
jacobian["y_4"]["y_14"] = zeros((1, 2), dtype=self.dtype)
jacobian["y_4"]["y_24"] = zeros((1, 1), dtype=self.dtype)
jacobian["y_4"]["y_34"] = zeros((1, 1), dtype=self.dtype)
return jacobian
[docs] def derive_blackbox_mission(self, x_shared, y_14, y_24, y_34):
"""THIS SECTION COMPUTES THE A/C RANGE from Breguet's law.
:param x_shared: shared design variable vector:
- x_shared[0]: thickness/chord ratio
- x_shared[1]: altitude
- x_shared[2]: Mach
- x_shared[3]: aspect ratio
- x_shared[4]: wing sweep
- x_shared[5]: wing surface area
:type x_shared: numpy array
:param y_14: shared variables coming from blackbox_structure:
- y_14[0]: total aircraft weight
- y_14[1]: fuel weight
:type y_14: numpy array
:param y_24: shared variables coming from
blackbox_aerodynamics (lift/drag ratio)
:param y_34: shared variables coming from
blackbox_propulsion (SFC)
:type y_34: numpy array
:returns: jacobian matrix of partial derivatives
:rtype: dict(dict(ndarray))
"""
jacobian = self.__initialize_jacobian()
sqrt_theta, dtheta_dh = self.compute_dtheta_dh(x_shared)
ac_range = self.compute_range(x_shared, y_14, y_24, y_34)
# dR_d(t/c) = 0
# jacobian['y_4']['x_shared'][0, 0] = 0
# dR_d(h)
jacobian["y_4"]["x_shared"][0, 1] = (
0.5 * ac_range * dtheta_dh / (sqrt_theta * sqrt_theta)
)
# dR_dM
jacobian["y_4"]["x_shared"][0, 2] = ac_range / x_shared[2]
# dR_dAR = 0
# jacobian['y_4']['x_shared'][0, 3] = 0
# dR_dsweep = 0
# jacobian['y_4']['x_shared'][0, 4] = 0
# dR_dsref = 0
# jacobian['y_4']['x_shared'][0, 5] = 0
# dR_dWt
dy4dy14 = self.compute_drange_dtotalweight(
x_shared, y_14, y_24, y_34, sqrt_theta
)
jacobian["y_4"]["y_14"][0, 0] = dy4dy14
# dR_dWf
dy4dy14 = self.compute_drange_dfuelweight(
x_shared, y_14, y_24, y_34, sqrt_theta
)
jacobian["y_4"]["y_14"][0, 1] = dy4dy14
# dR_d(L/D)
jacobian["y_4"]["y_24"][0, 0] = ac_range / y_24[0]
# dR_d(SFC)
jacobian["y_4"]["y_34"][0, 0] = -ac_range / y_34[0]
return jacobian