.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "examples/00-fluent/lunar_lander_thermal.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_examples_00-fluent_lunar_lander_thermal.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_examples_00-fluent_lunar_lander_thermal.py:

.. _ref_lunar_lander_thermal:

Thermal Model of a Lunar Lander Using the Monte Carlo Radiation Model
---------------------------------------------------------------------
This example demonstrates creating and solving a thermal model of a lander on
the lunar surface using Fluent's Monte Carlo radiation solver.

PyFluent uses the following loop at each timestep to retrofit the required
functionality to Fluent:

1. Calculate the current sun vector (direction of Sun relative to observer).
2. Update the radiation direction in Fluent.
3. Change the radiator emissivity depending on whether the louvers are open.
4. Run the solution for 1 timestep.

**Workflow tasks**

The Thermal Model of a Lunar Lander Using the Monte Carlo Radiation Model
example guides you through these tasks:

* Setting up a Monte Carlo radiation model.
* Creation of materials with thermal and radiation properties.
* Setting boundary conditions for heat transfer and radiation calculations.
* Setting up shell conduction boundary conditions.
* Calculating a solution using the pressure-based solver.
* Dynamically updating the Sun direction and lander state at each step.

**Problem description**

The lander is modelled as a hollow 1 m × 1 m × 1 m cube with aluminum walls 3
mm thick, covered in highly reflective multilayer insulation (MLI). To allow
for comparison to empirical data, the landing site is selected to be the same
as that of the Apollo 17 mission, which took measurements of the regolith
temperatures in the Taurus-Littrow valley located at 20.1908° N, 30.7717° E.

The lander has a radiator on the top surface of size 0.7 m × 0.7 m with louvers,
which open above 273 K to reject heat and close below 273 K to retain heat.
The PyFluent API is used to automatically update the radiator state and the
direction of the Sun at each timestep.

The case setup is taken from reference [1_], originally created in ANSYS
Thermal Desktop. It uses a thermal model of the lunar regolith developed in
reference [2_]. Validation data for the regolith temperatures are taken from
measurements conducted by the Apollo 17 mission to the Moon [3_].

.. GENERATED FROM PYTHON SOURCE LINES 68-70

.. code-block:: Python
   :dedent: 1




.. GENERATED FROM PYTHON SOURCE LINES 72-74

.. image:: ../../_static/lunar_lander_thermal_setup.png
  :align: center

.. GENERATED FROM PYTHON SOURCE LINES 76-85

Preliminary Setup
-----------------
First, we will define functions to compute the direction of the Sun relative
to the lander.

Perform required imports
~~~~~~~~~~~~~~~~~~~~~~~~
Perform required imports, including downloading the required geometry files.
The mesh has been pre-made for this problem.

.. GENERATED FROM PYTHON SOURCE LINES 85-107

.. code-block:: Python


    # flake8: noqa: E402

    from itertools import chain

    import numpy as np

    import ansys.fluent.core as pyfluent
    from ansys.fluent.core import examples

    lander_spaceclaim_file, lander_mesh_file, apollo17_temp_data = [
        examples.download_file(
            f,
            "pyfluent/lunar_lander_thermal",
        )
        for f in [
            "lander_geom.scdoc",
            "lander_mesh.msh.h5",
            "apollo17_temp_data.csv",
        ]
    ]


.. GENERATED FROM PYTHON SOURCE LINES 108-121

Define variables
~~~~~~~~~~~~~~~~
Define the key variables:

* The obliquity (axial tilt) of the Moon relative to the plane of the Earth's
  orbit around the Sun
* The setpoint temperature above which the radiator louvers open
* The coordinates of the lander
* The timestep size
* The number of timesteps

We will use a timestep size of 24 hours and run the simulation for 60 Earth
days, or approximately 2 lunar days.

.. GENERATED FROM PYTHON SOURCE LINES 121-138

.. code-block:: Python


    # Lunar axial obliquity relative to ecliptic
    moon_obliquity = np.deg2rad(1.54)

    # Louver setpoint temperature [K]
    louver_setpoint_temp = 273

    # Lander coordinates (Apollo 17 landing site)
    land_lat = np.deg2rad(20.1908)
    land_lon = np.deg2rad(30.7717)

    # Timestep size of 1 Earth day
    step_size = 86400

    # Run simulation for 60 Earth days
    n_steps = 60


.. GENERATED FROM PYTHON SOURCE LINES 139-153

Define sun vector function
~~~~~~~~~~~~~~~~~~~~~~~~~~
Define the function to calculate the Sun vector at the landing site. This is
a vector that points from the observer's location toward the Sun. It takes
Earth's ecliptic longitude at the beginning of the simulation, the subsolar
longitude at the beginning of the simulation, the observer's coordinates, and
the time elapsed in seconds since the beginning of the simulation as inputs.
It returns the Sun's altitude and azimuth as outputs.

For more information, see the following links:

* `Ecliptic longitude <https://w.wiki/7zFR>`_
* `Subsolar longitude <https://w.wiki/7zFS>`_
* `Altitude and azimuth <https://w.wiki/7zFU>`_

.. GENERATED FROM PYTHON SOURCE LINES 153-186

.. code-block:: Python



    def calc_sun_vecs_for_moon(
        earth_ecliptic_lon_start: float,
        subsol_lon_start: float,
        obsv_lat: float,
        obsv_lon: float,
        t: float,
    ) -> tuple[float, float]:
        """Calculate sun vectors."""
        # Earth ecliptic longitude
        earth_ecliptic_lon = earth_ecliptic_lon_start + 2 * np.pi / (365 * 86400) * t

        # Subsolar point
        subsol_lat = np.arcsin(np.sin(-moon_obliquity) * np.sin(earth_ecliptic_lon))
        subsol_lon = subsol_lon_start + t / (29.5 * 86400) * 2 * np.pi

        # Solar altitude and azimuth
        sun_obsv_phaseang = np.arccos(
            np.sin(subsol_lat) * np.sin(obsv_lat)
            + np.cos(subsol_lat) * np.cos(obsv_lat) * np.cos(obsv_lon - subsol_lon)
        )

        sun_alt = np.pi / 2 - sun_obsv_phaseang
        sun_azm = np.arctan2(
            np.cos(obsv_lat) * np.sin(subsol_lat)
            - np.sin(obsv_lat) * np.cos(subsol_lat) * np.cos(subsol_lon - obsv_lon),
            np.cos(subsol_lat) * np.sin(subsol_lon - obsv_lon),
        )

        return sun_alt, sun_azm



.. GENERATED FROM PYTHON SOURCE LINES 187-196

Define beam direction function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Define the function that converts the Sun's altitude and azimuth, as
calculated by ``calc_sun_vecs_for_moon``, into a beam direction in Cartesian
coordinates that can be used in Fluent. The coordinate system used is:

* X: North
* Y: Zenith
* Z: East

.. GENERATED FROM PYTHON SOURCE LINES 196-216

.. code-block:: Python



    def sun_vec_to_beam_dir(
        sun_alt: float,
        sun_azm: float,
    ) -> tuple[float, float, float]:
        """Calculate beam direction."""
        # Coordinate system:
        #   X: North
        #   Y: Zenith
        #   Z: East
        x = np.cos(sun_alt) * np.cos(sun_azm)
        y = np.sin(sun_alt)
        z = np.cos(sun_alt) * np.sin(sun_azm)

        # Since the sun vector points toward the sun, we need to take the
        # negative to get the beam direction
        return -x, -y, -z



.. GENERATED FROM PYTHON SOURCE LINES 217-223

Define mean surface temperature function
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The radiator will be opened or closed based on its mean temperature. We will
access the solver session object to extract nodal temperatures. It takes a
list of surface names as input, finds their surface IDs, obtains the scalar
field data from the solver, then returns the average temperature.

.. GENERATED FROM PYTHON SOURCE LINES 223-256

.. code-block:: Python



    def get_surf_mean_temp(
        surf_names: list[str],
        solver: pyfluent.session_solver.Solver,
    ) -> float:
        """Calculate mean surface temperature."""
        # Get surface IDs
        surfs = solver.field_info.get_surfaces_info()
        surf_ids_ = [surfs[surf_name]["surface_id"] for surf_name in surf_names]

        # Flatten surf_ids nested list
        surf_ids = list(chain(*surf_ids_))

        # Get temperature data
        temp_data = solver.field_data.get_scalar_field_data(
            "temperature",
            surfaces=surf_ids,
        )

        # Calculate mean temperature across surfaces
        temps = np.array([])
        for x in temp_data.values():
            temps = np.concatenate(
                (
                    temps,
                    np.array([y.scalar_data for y in x.data]),
                ),
            )

        return np.mean(temps)



.. GENERATED FROM PYTHON SOURCE LINES 257-263

Start Fluent
------------
We are now ready to launch Fluent and load the mesh.

Launch Fluent and print Fluent version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. GENERATED FROM PYTHON SOURCE LINES 263-273

.. code-block:: Python


    solver = pyfluent.launch_fluent(
        precision="double",
        processor_count=12,
        mode="solver",
        cwd=pyfluent.EXAMPLES_PATH,
        product_version="25.1.0",
    )
    print(solver.get_fluent_version())


.. GENERATED FROM PYTHON SOURCE LINES 274-276

Load the mesh
~~~~~~~~~~~~~

.. GENERATED FROM PYTHON SOURCE LINES 276-279

.. code-block:: Python


    solver.file.read_mesh(file_name=lander_mesh_file)


.. GENERATED FROM PYTHON SOURCE LINES 280-290

Case Setup
----------
We are now ready to begin setting up the case.

Transient settings
~~~~~~~~~~~~~~~~~~
Set the solution type to transient and configure. We will use 2nd-order time-
stepping, a fixed timestep size, and a limit of 20 iterations per timestep.
Since we are only running 1 timestep at a time, the time step count is set to
1.

.. GENERATED FROM PYTHON SOURCE LINES 290-301

.. code-block:: Python


    # Set solution to transient
    solver.setup.general.solver.time = "unsteady-2nd-order"

    # Set transient settings
    trans_controls = solver.solution.run_calculation.transient_controls
    trans_controls.type = "Fixed"
    trans_controls.max_iter_per_time_step = 20
    trans_controls.time_step_count = 1
    trans_controls.time_step_size = step_size


.. GENERATED FROM PYTHON SOURCE LINES 302-306

Enable models
~~~~~~~~~~~~~
Enable the energy model. Since fluid flow is not simulated, we will set the
viscosity model to laminar.

.. GENERATED FROM PYTHON SOURCE LINES 306-311

.. code-block:: Python


    models = solver.setup.models
    models.energy.enabled = True
    models.viscous.model = "laminar"


.. GENERATED FROM PYTHON SOURCE LINES 312-323

Set up radiation model
~~~~~~~~~~~~~~~~~~~~~~
Enable the Monte Carlo radiation model with two radiation bands: one for
solar radiation and one for thermal infrared radiation. Ensure that bands are
created in order of increasing wavelength.

The number of histories is set to 10 million to reduce computation time, but
more may be required for accurate results.

The limits of each band are based on Fluent manual recommendations and on
space industry best practices [4_].

.. GENERATED FROM PYTHON SOURCE LINES 323-346

.. code-block:: Python



    # Set up radiation model
    radiation = models.radiation
    radiation.model = "monte-carlo"
    radiation.monte_carlo.number_of_histories = 1e7

    # Define range of solar wavelengths
    radiation.multiband["solar"] = {
        "start": 0,
        "end": 2.8,
    }
    # Define range of thermal IR wavelengths
    radiation.multiband["thermal-ir"] = {
        "start": 2.8,
        "end": 100,
    }

    # Solve radiation once per timestep
    radiation_freq = radiation.solve_frequency
    radiation_freq.method = "time-step"
    radiation_freq.time_step_interval = 1


.. GENERATED FROM PYTHON SOURCE LINES 347-353

Define materials
~~~~~~~~~~~~~~~~
Create materials to represent the vacuum of space (to prevent thermal
conduction through the cell zone representing vacuum) and the lunar regolith
(soil). The thermal conductivity of the regolith and the surface 'fluff' is
strongly temperature-dependent and so must be modelled using an expression.

.. GENERATED FROM PYTHON SOURCE LINES 353-391

.. code-block:: Python


    # --- Properties of vacuum ---
    # Thermal conductivity: 0

    vacuum = solver.setup.materials.solid.create("vacuum")
    vacuum.chemical_formula = ""
    vacuum.thermal_conductivity.value = 0
    vacuum.absorption_coefficient.value = 0
    vacuum.refractive_index.value = 1

    # --- Properties of fluff (see ref. [2]) ---
    # Density: 1000 [kg m^-3]
    # Specific heat capacity: 1050 [J kg^-1 K^-1]
    # Thermal conductivity: 9.22e-4*(1 + 1.48*(temperature/350 K)^3) [W m^-1 K^-1]

    fluff = solver.setup.materials.solid.create("fluff")
    fluff.chemical_formula = ""
    fluff.density.value = 1000
    fluff.specific_heat.value = 1050
    fluff.thermal_conductivity.option = "expression"
    fluff.thermal_conductivity.expression = (
        "9.22e-4[W m^-1 K^-1]*(1 + 1.48*(StaticTemperature/350[K])^3)"
    )

    # --- Properties of regolith (see ref. [2]) ---
    # Density: 2000 [kg m^-3]
    # Specific heat capacity: 1050 [J kg^-1 K^-1]
    # Thermal conductivity: 9.30e-4*(1 + 0.73*(temperature/350 K)^3) [W m^-1 K^-1]

    regolith = solver.setup.materials.solid.create("regolith")
    regolith.chemical_formula = ""
    regolith.density.value = 2000
    regolith.specific_heat.value = 1050
    regolith.thermal_conductivity.option = "expression"
    regolith.thermal_conductivity.expression = (
        "9.30e-4[W m^-1 K^-1]*(1 + 0.73*(StaticTemperature/350[K])^3)"
    )


.. GENERATED FROM PYTHON SOURCE LINES 392-398

Cell zone conditions
~~~~~~~~~~~~~~~~~~~~
Since the Monte Carlo radiation model only supports radiative transfer
through a cell zone, a cell zone is used to model the vacuum around the
lander. This cell zone must be set to be a solid so that the fluid equations
are not solved there, then it must be assigned to the vacuum material.

.. GENERATED FROM PYTHON SOURCE LINES 398-406

.. code-block:: Python


    cellzones = solver.setup.cell_zone_conditions
    cellzones.set_zone_type(
        zone_list=["geom-2_domain"],
        new_type="solid",
    )
    cellzones.solid["geom-2_domain"].material = "vacuum"


.. GENERATED FROM PYTHON SOURCE LINES 407-413

Regolith boundary condition
~~~~~~~~~~~~~~~~~~~~~~~~~~~
We will implement the regolith model in reference [2_] by modelling
it as a wall exhibiting multilayer shell conduction. A basal heat flux is
used to represent the geothermal heat from the Moon's interior that heats
the regolith from the bottom.

.. GENERATED FROM PYTHON SOURCE LINES 413-451

.. code-block:: Python


    # --- Regolith BC ---
    # Thickness of layers: 0.02, 0.04, 0.08, 0.16, 0.32 [m]
    # Heating at base: 0.031 [W m^-2]
    # Surface absorptivity: 0.87
    # Surface emissivity: 0.97

    regolith_bc = solver.setup.boundary_conditions.wall["regolith"]

    regolith_bc.thermal.q.value = 0.031
    regolith_bc.thermal.planar_conduction = True
    regolith_bc.thermal.shell_conduction = [
        {
            "thickness": 0.02,
            "material": "fluff",
        },
        {
            "thickness": 0.04,
            "material": "regolith",
        },
        {
            "thickness": 0.08,
            "material": "regolith",
        },
        {
            "thickness": 0.16,
            "material": "regolith",
        },
        {
            "thickness": 0.32,
            "material": "regolith",
        },
    ]
    regolith_bc.radiation.band_in_emiss = {
        "solar": 0.87,
        "thermal-ir": 0.97,
    }


.. GENERATED FROM PYTHON SOURCE LINES 452-456

Space boundary condition
~~~~~~~~~~~~~~~~~~~~~~~~
The space boundary condition represents deep space and also acts as the
source of the Sun's illumination in the simulation.

.. GENERATED FROM PYTHON SOURCE LINES 456-479

.. code-block:: Python


    # --- Set up space boundary condition ---
    # Temperature: 3 [K]
    # Emissivity: 1
    # Absorptivity: 1
    # Solar flux: 1414 [W m^-2]

    space_bc = solver.setup.boundary_conditions.wall["space"]

    space_bc.thermal.thermal_bc = "Temperature"
    space_bc.thermal.t.value = 3
    space_bc.thermal.material = "vacuum"
    space_bc.radiation.mc_bsource_p = True
    space_bc.radiation.band_q_irrad["solar"].value = 1414
    space_bc.radiation.band_diffuse_frac = {
        "solar": 0,
        "thermal-ir": 0,
    }
    space_bc.radiation.band_in_emiss = {
        "solar": 1,
        "thermal-ir": 1,
    }


.. GENERATED FROM PYTHON SOURCE LINES 480-484

Spacecraft walls boundary condition
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The spacecraft is covered in reflective MLI (multilayer insulation) and heat
transfer through the aluminum shell can be simulated using shell conduction.

.. GENERATED FROM PYTHON SOURCE LINES 484-505

.. code-block:: Python


    # --- Set up spacecraft shell boundary condition ---
    # Thickness: 0.03 [m]
    # Material: aluminum
    # Absorptivity: 0.05
    # Emissivity: 0.05

    sc_mli_bc = solver.setup.boundary_conditions.wall["sc-mli"]

    sc_mli_bc.thermal.planar_conduction = True
    sc_mli_bc.thermal.shell_conduction = [
        {
            "thickness": 0.03,
            "material": "aluminum",
        },
    ]
    sc_mli_bc.radiation.band_in_emiss = {
        "solar": 0.05,
        "thermal-ir": 0.05,
    }


.. GENERATED FROM PYTHON SOURCE LINES 506-511

Spacecraft radiator boundary condition
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The spacecraft's radiator is modelled in a similar way to the spacecraft's
walls, but the emissivity is left unset as it will be changed dynamically by
our PyFluent script depending on its temperature during the simulation.

.. GENERATED FROM PYTHON SOURCE LINES 511-531

.. code-block:: Python


    # --- Set up spacecraft radiator boundary condition ---
    # Thickness: 0.03 [m]
    # Material: aluminum
    # Absorptivity: 0.17
    # Emissivity: 0.09 below 273 K, 0.70 otherwise

    sc_rad_bc = solver.setup.boundary_conditions.wall["sc-radiator"]

    sc_rad_bc.thermal.planar_conduction = True
    sc_rad_bc.thermal.shell_conduction = [
        {
            "thickness": 0.03,
            "material": "aluminum",
        },
    ]
    sc_rad_bc.radiation.band_in_emiss = {
        "solar": 0.17,
    }


.. GENERATED FROM PYTHON SOURCE LINES 532-538

Initialize simulation
~~~~~~~~~~~~~~~~~~~~~
Initialize the simulation. Doing so now will create conductive zones between
shell layers representing the regolith, allowing us to make report
definitions on them in the next step. The entire domain will be initialized
to a temperature of 230 K, or -43 °C.

.. GENERATED FROM PYTHON SOURCE LINES 538-543

.. code-block:: Python


    sim_init = solver.solution.initialization
    sim_init.defaults["temperature"] = 230
    sim_init.initialize()


.. GENERATED FROM PYTHON SOURCE LINES 544-547

Spacecraft temperature reports
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create reports for the spacecraft's minimum, mean, and maximum temperatures.

.. GENERATED FROM PYTHON SOURCE LINES 547-568

.. code-block:: Python


    surf_report_defs = solver.solution.report_definitions.surface

    sc_surfs = ["sc-radiator", "sc-mli"]

    surf_report_defs["sc-min-temp"] = {
        "surface_names": sc_surfs,
        "report_type": "surface-facetmin",
        "field": "temperature",
    }
    surf_report_defs["sc-avg-temp"] = {
        "surface_names": sc_surfs,
        "report_type": "surface-facetavg",
        "field": "temperature",
    }
    surf_report_defs["sc-max-temp"] = {
        "surface_names": sc_surfs,
        "report_type": "surface-facetmax",
        "field": "temperature",
    }


.. GENERATED FROM PYTHON SOURCE LINES 569-573

Regolith temperature reports
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Create reports for the mean temperatures of each regolith layer. We will
store the report names for use later.

.. GENERATED FROM PYTHON SOURCE LINES 573-592

.. code-block:: Python


    surf_report_defs = solver.solution.report_definitions.surface

    # Loop over all regolith reports to set common properties
    regolith_report_names = []
    for i in range(1, 5 + 1):
        report_name = f"regolith-layer-{i}-temp"
        surf_report_defs[report_name] = {
            "report_type": "surface-facetavg",
            "field": "temperature",
        }
        regolith_report_names.extend([report_name])

    surf_report_defs["regolith-layer-1-temp"].surface_names = ["regolith"]
    surf_report_defs["regolith-layer-2-temp"].surface_names = ["regolith-1:2"]
    surf_report_defs["regolith-layer-3-temp"].surface_names = ["regolith-2:3"]
    surf_report_defs["regolith-layer-4-temp"].surface_names = ["regolith-3:4"]
    surf_report_defs["regolith-layer-5-temp"].surface_names = ["regolith-4:5"]


.. GENERATED FROM PYTHON SOURCE LINES 593-596

Temperature report files
~~~~~~~~~~~~~~~~~~~~~~~~
Create temperature report files for post-processing.

.. GENERATED FROM PYTHON SOURCE LINES 596-609

.. code-block:: Python


    surf_report_files = solver.solution.monitor.report_files

    # Spacecraft temperatures
    surf_report_files["sc-temps-rfile"] = {
        "report_defs": ["flow-time", "sc-min-temp", "sc-avg-temp", "sc-max-temp"],
    }

    # Regolith temperatures
    surf_report_files["regolith-temps-rfile"] = {
        "report_defs": [*regolith_report_names, "flow-time"],
    }


.. GENERATED FROM PYTHON SOURCE LINES 610-613

Autosave
~~~~~~~~
Set the case to save only the data file at each timestep for post-processing.

.. GENERATED FROM PYTHON SOURCE LINES 613-621

.. code-block:: Python


    autosave = solver.file.auto_save

    autosave.case_frequency = "if-mesh-is-modified"
    autosave.data_frequency = 1
    autosave.save_data_file_every.frequency_type = "time-step"
    autosave.append_file_name_with.file_suffix_type = "time-step"


.. GENERATED FROM PYTHON SOURCE LINES 622-626

Convergence criteria
~~~~~~~~~~~~~~~~~~~~
Turn off the convergence criteria pertaining to fluid flow as there is no
fluid flow in this simulation. Keep only the energy convergence criterion.

.. GENERATED FROM PYTHON SOURCE LINES 626-633

.. code-block:: Python


    residuals = solver.solution.monitor.residual.equations

    for criterion in ["continuity", "x-velocity", "y-velocity", "z-velocity"]:
        residuals[criterion].check_convergence = False
        residuals[criterion].monitor = False


.. GENERATED FROM PYTHON SOURCE LINES 634-637

Write case file
~~~~~~~~~~~~~~~
Write the case file. Enable overwrite.

.. GENERATED FROM PYTHON SOURCE LINES 637-644

.. code-block:: Python


    solver.file.batch_options.confirm_overwrite = True
    solver.file.write(
        file_name="lunar_lander_thermal.cas.h5",
        file_type="case",
    )


.. GENERATED FROM PYTHON SOURCE LINES 645-654

Solve
-----

Solve case
~~~~~~~~~~
Run the case, using a loop to update the radiator state and Sun vector at
each timestep. We will assume that the Earth's ecliptic longitude and the
subsolar longitude are both zero at the start of the simulation for
simplicity.

.. GENERATED FROM PYTHON SOURCE LINES 654-697

.. code-block:: Python


    for i in range(n_steps):
        # Get current simulation time
        t = solver.rp_vars("flow-time")

        # Calculate sun vector
        sun_alt, sun_azm = calc_sun_vecs_for_moon(
            earth_ecliptic_lon_start=0,
            subsol_lon_start=0,
            obsv_lat=land_lat,
            obsv_lon=land_lon,
            t=t,
        )
        beam_x, beam_y, beam_z = sun_vec_to_beam_dir(
            sun_alt=sun_alt,
            sun_azm=sun_azm,
        )

        # Set beam direction
        solver.setup.boundary_conditions.wall["space"].radiation.reference_direction = [
            beam_x,
            beam_y,
            beam_z,
        ]

        # Calculate radiator mean temperature
        rad_mean_temp = get_surf_mean_temp(
            ["sc-radiator"],
            solver,
        )

        # Simulate closing louvers below 273 K by changing emissivity
        rad_emiss = solver.setup.boundary_conditions.wall[
            "sc-radiator"
        ].radiation.band_in_emiss["thermal-ir"]
        if rad_mean_temp < 273:
            rad_emiss.value = 0.09
        else:
            rad_emiss.value = 0.70

        # Run simulation for 1 timestep
        solver.solution.run_calculation.calculate()


.. GENERATED FROM PYTHON SOURCE LINES 698-701

Close Fluent
~~~~~~~~~~~~
Shut down the solver.

.. GENERATED FROM PYTHON SOURCE LINES 701-704

.. code-block:: Python


    solver.exit()


.. GENERATED FROM PYTHON SOURCE LINES 705-712

Post-process
------------
Post-process the data.

Import packages
~~~~~~~~~~~~~~~
Import the packages required for post-processing.

.. GENERATED FROM PYTHON SOURCE LINES 712-719

.. code-block:: Python


    from pathlib import Path
    import re

    from matplotlib import pyplot as plt
    import pandas as pd


.. GENERATED FROM PYTHON SOURCE LINES 720-725

Clean column names function
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Pandas will not correctly format the column names when reading in the Fluent
output files. We will clean the names by removing all parentheses and double
quotation marks.

.. GENERATED FROM PYTHON SOURCE LINES 725-732

.. code-block:: Python



    def clean_col_names(df):
        """Clean column names."""
        df.columns = [re.sub(r'["()]', "", col) for col in df.columns]



.. GENERATED FROM PYTHON SOURCE LINES 733-740

Read in simulation data
~~~~~~~~~~~~~~~~~~~~~~~
Read the Fluent output files for the regolith and spacecraft temperature data
into Pandas dataframes. The separation delimiter between columns is defined
as one or more spaces that is not immediately in front of or behind an
alphabetical character, implemented as negative lookarounds in a regular
expression.

.. GENERATED FROM PYTHON SOURCE LINES 740-764

.. code-block:: Python


    root = Path(pyfluent.EXAMPLES_PATH)
    sep = r"(?<![a-zA-Z])\s+(?![a-zA-Z])"

    # Read in regolith data
    regolith_df = pd.read_csv(
        root / "regolith-temps-rfile.out",
        sep=sep,
        header=2,
        dtype=np.float64,
        engine="python",
    )
    clean_col_names(regolith_df)

    # Read in spacecraft data
    sc_df = pd.read_csv(
        root / "sc-temps-rfile.out",
        sep=sep,
        header=2,
        dtype=np.float64,
        engine="python",
    )
    clean_col_names(sc_df)


.. GENERATED FROM PYTHON SOURCE LINES 765-770

Read in experimental data
~~~~~~~~~~~~~~~~~~~~~~~~~
Read in the experimental data from Apollo 17 [3_] to compare against the
simulation. We will offset the data to start at time step 25 of the
simulation, to ensure that the data are compared at the same sun angles.

.. GENERATED FROM PYTHON SOURCE LINES 770-778

.. code-block:: Python


    apollo17_df = pd.read_csv(apollo17_temp_data)

    apollo17_offset = regolith_df["flow-time"].iloc[25]
    apollo17_df["Offset time since sunrise"] = (
        apollo17_df["Time since sunrise"] + apollo17_offset
    )


.. GENERATED FROM PYTHON SOURCE LINES 779-782

Set data types
~~~~~~~~~~~~~~
Set the data type of the time step column to integer.

.. GENERATED FROM PYTHON SOURCE LINES 782-794

.. code-block:: Python


    regolith_df = regolith_df.astype(
        {
            "Time Step": np.int64,
        },
    )
    sc_df = sc_df.astype(
        {
            "Time Step": np.int64,
        },
    )


.. GENERATED FROM PYTHON SOURCE LINES 795-799

Plot regolith temperatures
~~~~~~~~~~~~~~~~~~~~~~~~~~
Plot the mean temperatures of each layer of regolith, as well as a comparison
with the Apollo 17 data. The simulation agrees well with experiment.

.. GENERATED FROM PYTHON SOURCE LINES 799-819

.. code-block:: Python


    fig1, ax1 = plt.subplots()

    regolith_df.plot(
        x="flow-time",
        y=[f"regolith-layer-{i+1}-temp" for i in range(5)],
        title="Regolith temperatures",
        ax=ax1,
    )
    apollo17_df.plot(
        x="Offset time since sunrise",
        y="Temperature",
        style="x",
        xlabel="Time [s]",
        ylabel="Temperature [K]",
        ax=ax1,
    )

    ax1.legend(["Layer 1", "2", "3", "4", "5", "Apollo 17"])


.. GENERATED FROM PYTHON SOURCE LINES 820-823

Plot spacecraft temperatures
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Plot the minimum, mean, and maximum temperatures of the spacecraft.

.. GENERATED FROM PYTHON SOURCE LINES 823-838

.. code-block:: Python


    fig2, ax2 = plt.subplots()

    sc_df.plot(
        x="flow-time",
        y=["sc-min-temp", "sc-avg-temp", "sc-max-temp"],
        title="Spacecraft Temperatures",
        xlabel="Time [s]",
        ylabel="Temperature [K]",
        ax=ax2,
    )
    ax2.axhline(273, color="k", linestyle=":")

    ax2.legend(["Minimum", "Mean", "Maximum", "Setpoint"])


.. GENERATED FROM PYTHON SOURCE LINES 839-842

Show plots
~~~~~~~~~~
Display the plots.

.. GENERATED FROM PYTHON SOURCE LINES 842-845

.. code-block:: Python


    plt.show()


.. GENERATED FROM PYTHON SOURCE LINES 846-858

The expected plot for the mean temperature of the regolith layers (Figure 1)
is as follows. This represents the regolith temperatures that would be
observed without a spacecraft in the environment.

.. image:: ../../_static/lunar_lander_regolith_temps.png
  :align: center

The expected plot for the spacecraft's minimum, mean, and maximum
temperatures (Figure 2) is as follows.

.. image:: ../../_static/lunar_lander_sc_temps.png
  :align: center

.. GENERATED FROM PYTHON SOURCE LINES 861-892

References
------------

.. _1:

[1] T.-Y. Park, J.-J. Lee, J.-H. Kim, and H.-U. Oh, “Preliminary Thermal
Design and Analysis of Lunar Lander for Night Survival,” *International
Journal of Aerospace Engineering*, vol. 2018, p. e4236396, Oct. 2018, doi:
`doi.org/10.1155/2018/4236396 <https://doi.org/10.1155/2018/4236396>`_.

.. _2:

[2] R. J. Christie, D. W. Plachta, and M. M. Yasan, “Transient Thermal Model
and Analysis of the Lunar Surface and Regolith for Cryogenic Fluid Storage,”
NASA Glenn Research Center, Cleveland, Ohio, NASA Technical Report
TM-2008-215300, Aug. 2008. [Online]. Available:
https://ntrs.nasa.gov/citations/20080039640

.. _3:

[3] M. G. Langseth, S. J. Keihm, and J. L. Chute, “Heat Flow Experiment,” in
*Apollo 17: Preliminary Science Report*, vol. SP-330, Washington, D.C.: NASA
Lyndon B. Johnson Space Center, 1973. [Online]. Available:
https://ui.adsabs.harvard.edu/abs/1973NASSP.330....../abstract

.. _4:

[4] L. Kauder, “Spacecraft Thermal Control Coatings References,” Goddard
Space Flight Center, Greenbelt, MD 20771, NASA Technical Report
NASA/TP-2005-212792, Dec. 2005. [Online]. Available:
https://ntrs.nasa.gov/citations/20070014757


.. _sphx_glr_download_examples_00-fluent_lunar_lander_thermal.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: lunar_lander_thermal.ipynb <lunar_lander_thermal.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: lunar_lander_thermal.py <lunar_lander_thermal.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: lunar_lander_thermal.zip <lunar_lander_thermal.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_