Heating and Cooling in UCLCHEM#

This notebook demonstheating how to use UCLCHEM with heating and cooling mechanisms enabled. UCLCHEM includes a comprehensive set of heating and cooling processes that can significantly affect the temperature evolution and chemistry in astrophysical environments.

Overview#

UCLCHEM can track gas temperature changes due to:

Heating processes:

  • Photoelectric heating from PAHs and dust grains

  • H₂ formation heating

  • H₂ photodissociation heating

  • Cosmic ray heating

  • Chemical reaction enthalpy (optional)

  • X-ray heating

  • Turbulent heating

  • And more…

Cooling processes:

  • Line cooling (CO, H₂O, OH, etc.)

  • Continuum cooling from dust

  • Atomic fine structure cooling (C⁺, O, etc.)

  • Recombination cooling

  • And more…

By default, heating is enabled (heatingFlag=True), but you can turn it off or customize which processes are included.

import uclchem
import matplotlib.pyplot as plt

Example 1: Basic Model with Heating Enabled#

Let’s start with a simple cloud model with heating and cooling enabled (the default behavior). We’ll model a molecular cloud core at constant density.

# Define parameters for a molecular cloud with heating enabled
param_dict_heating = {
    "initialDens": 1e4,  # Initial density (cm^-3)
    "initialTemp": 10.0,  # Initial temperature (K)
    "finalTime": 1.0e6,  # Final time (years)
    "baseAv": 10.0,  # Visual extinction
    "rout": 0.1,  # Outer radius (pc)
    "freefall": False,  # Keep constant density
    "heatingFlag": True,  # Enable heating (default)
}

# Run the model using the Cloud class
cloud = uclchem.model.Cloud(param_dict=param_dict_heating)

# Extract data from the model object
physics, abundances = cloud.get_dataframes(joined=False)
_, _, rates = cloud.get_dataframes(joined=False, with_rates=True)
_, _, heating = cloud.get_dataframes(joined=False, with_heating=True)
start_abund = cloud.next_starting_chemistry_array
flag = 0 if cloud.has_attr("_data") else -1

print(f"Model completed with flag: {flag}")
if flag < 0:
    print(f"Error: Model failed to complete")
else:
    print("Success! Model completed.")
    print(
        f"\nTemperature range: {physics['gasTemp'].min():.2f} - {physics['gasTemp'].max():.2f} K"
    )
    print(
        f"Density range: {physics['Density'].min():.2e} - {physics['Density'].max():.2e} cm^-3"
    )
Model completed with flag: -1
Error: Model failed to complete

Visualizing Temperature Evolution#

The physics DataFrame contains physical properties including gas temperature. Let’s plot how temperature evolves over time.

# Plot temperature evolution
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(physics["Time"], physics["gasTemp"], linewidth=2, color="darkred")
ax.set_xlabel("Time (years)", fontsize=12)
ax.set_ylabel("Gas Temperature (K)", fontsize=12)
ax.set_xscale("log")
ax.set_title("Gas Temperature Evolution with Heating/Cooling", fontsize=14)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
../_images/d3f0f856ecebb6668cdce41587fd1111d27673f92a4c64b34f660fb7c09c532b.png

Example 2: Comparing Heating On vs Off#

To see the impact of heating/cooling processes, let’s run two models side by side: one with heating enabled and one without.

# Model WITHOUT heating
param_dict_no_heating = param_dict_heating.copy()
param_dict_no_heating["heatingFlag"] = False

cloud_no_heat = uclchem.model.Cloud(param_dict=param_dict_no_heating)

# Extract data
physics_no_heat, abundances_no_heat = cloud_no_heat.get_dataframes(joined=False)
_, _, rates_no_heat = cloud_no_heat.get_dataframes(joined=False, with_rates=True)
_, _, heating_no_heat = cloud_no_heat.get_dataframes(joined=False, with_heating=True)
flag_no_heat = 0 if cloud_no_heat.has_attr("_data") else -1

print(f"No heating model completed with flag: {flag_no_heat}")

# Compare the two runs
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Temperature comparison
axes[0].plot(physics["Time"], physics["gasTemp"], label="Heating ON", linewidth=2)
axes[0].plot(
    physics_no_heat["Time"],
    physics_no_heat["gasTemp"],
    label="Heating OFF",
    linewidth=2,
    linestyle="--",
)
axes[0].set_xlabel("Time (years)", fontsize=12)
axes[0].set_ylabel("Gas Temperature (K)", fontsize=12)
axes[0].set_xscale("log")
axes[0].set_title("Temperature Evolution", fontsize=14)
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# Compare a key chemical species (CO)
axes[1].plot(physics["Time"], abundances["H2O"], label="Heating ON", linewidth=2)
axes[1].plot(
    physics_no_heat["Time"],
    abundances_no_heat["H2O"],
    label="Heating OFF",
    linewidth=2,
    linestyle="--",
)
axes[1].set_xlabel("Time (years)", fontsize=12)
axes[1].set_ylabel("H$_2$O Abundance", fontsize=12)
axes[1].set_xscale("log")
axes[1].set_yscale("log")
axes[1].set_title("CO Abundance Evolution", fontsize=14)
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("\nFinal temperatures:")
print(f"  With heating: {physics['gasTemp'].iloc[-1]:.2f} K")
print(f"  Without heating: {physics_no_heat['gasTemp'].iloc[-1]:.2f} K")
No heating model completed with flag: -1

Final temperatures:
  With heating: 13.10 K
  Without heating: 10.00 K
../_images/7bb856c2f2c41a8fb9b00eec0a5a0c5757656e5e05f299c1db0e00a141249a9f.png

Example 3: Accessing Heating and Cooling Rates#

UCLCHEM can return detailed heating and cooling rates when you use return_dataframe=True with the rates arrays. The heating DataFrame includes individual contributions from each heating and cooling process.

# The heating DataFrame contains heating and cooling information
print("Available heating/cooling columns:")
print(heating.columns.tolist()[:20], "...")  # Show first 20 columns

# Extract heating and cooling columns
heating_cols = [
    col for col in heating.columns if "heating" in col.lower() and col != "Time"
]
cooling_cols = [col for col in heating.columns if "cooling" in col.lower()]

print(f"\nFound {len(heating_cols)} heating processes")
print(f"Found {len(cooling_cols)} cooling processes")

# Show the heating processes
print("\nHeating processes:")
for col in heating_cols[:10]:  # Show first 10
    print(f"  - {col}")
Available heating/cooling columns:
['Time', 'AtomicLineEmission Cooling', 'H2CollisionallyInduced Cooling', 'Compton Cooling', 'ContinuumEmission Cooling', 'MolecularLine Cooling', 'H Line Cooling', 'C+ Line Cooling', 'O Line Cooling', 'C Line Cooling', 'CO Line Cooling', 'p-H2 Line Cooling', 'o-H2 Line Cooling', 'PhotoelectricBakes Heating', 'PhotoelectricWeingartner Heating', 'H2Formation Heating', 'H2Photodissociation Heating', 'H2FUVPumping Heating', 'CarbonIonization Heating', 'CosmicRay Heating'] ...

Found 10 heating processes
Found 12 cooling processes

Heating processes:
  - PhotoelectricBakes Heating
  - PhotoelectricWeingartner Heating
  - H2Formation Heating
  - H2Photodissociation Heating
  - H2FUVPumping Heating
  - CarbonIonization Heating
  - CosmicRay Heating
  - Turbulent Heating
  - GasGrainCollisions Heating
  - Chemical Heating
heating
Time AtomicLineEmission Cooling H2CollisionallyInduced Cooling Compton Cooling ContinuumEmission Cooling MolecularLine Cooling H Line Cooling C+ Line Cooling O Line Cooling C Line Cooling ... PhotoelectricBakes Heating PhotoelectricWeingartner Heating H2Formation Heating H2Photodissociation Heating H2FUVPumping Heating CarbonIonization Heating CosmicRay Heating Turbulent Heating GasGrainCollisions Heating Chemical Heating
0 1.000000e-07 0.0 0.0 7.487719e-35 2.985107e-63 1.166008e-24 0.0 0.0 3.310983e-25 8.348479e-25 ... -1.100196e-34 0.0 3.755034e-21 0.0 0.0 4.513450e-48 1.041415e-24 7.000000e-40 0.000000e+00 0.0
1 1.000000e-06 0.0 0.0 7.487719e-35 2.985107e-63 1.165998e-24 0.0 0.0 3.310983e-25 8.348376e-25 ... -1.100196e-34 0.0 3.755034e-21 0.0 0.0 4.515769e-48 3.030519e-24 7.000000e-40 -9.310917e-34 0.0
2 1.000000e-05 0.0 0.0 7.487720e-35 2.985108e-63 1.166017e-24 0.0 0.0 3.310984e-25 8.348549e-25 ... -1.100196e-34 0.0 3.755034e-21 0.0 0.0 4.536644e-48 3.030519e-24 7.000000e-40 -9.315356e-33 0.0
3 1.000000e-04 0.0 0.0 7.487720e-35 2.985108e-63 1.166017e-24 0.0 0.0 3.310997e-25 8.348518e-25 ... -1.100196e-34 0.0 3.755034e-21 0.0 0.0 4.745404e-48 3.030519e-24 7.000000e-40 -9.315801e-32 0.0
4 1.000000e-03 0.0 0.0 7.487723e-35 2.985113e-63 1.166047e-24 0.0 0.0 3.311123e-25 8.348392e-25 ... -1.100197e-34 0.0 3.755034e-21 0.0 0.0 6.833985e-48 3.030519e-24 7.000000e-40 -9.315847e-31 0.0
5 1.000000e-02 0.0 0.0 7.487753e-35 2.985165e-63 1.166447e-24 0.0 0.0 3.312383e-25 8.348272e-25 ... -1.100209e-34 0.0 3.755033e-21 0.0 0.0 2.780588e-47 3.030519e-24 7.000000e-40 -9.315871e-30 0.0
6 1.000000e-01 0.0 0.0 7.488048e-35 2.985680e-63 1.170624e-24 0.0 0.0 3.324984e-25 8.348308e-25 ... -1.100331e-34 0.0 3.755032e-21 0.0 0.0 2.406090e-46 3.030519e-24 7.000000e-40 -9.316070e-29 0.0
7 1.000000e+00 0.0 0.0 7.490966e-35 2.990834e-63 1.212569e-24 0.0 0.0 3.453174e-25 8.349198e-25 ... -1.101548e-34 0.0 3.755022e-21 0.0 0.0 2.374056e-45 3.030524e-24 7.000000e-40 -9.318054e-28 0.0
8 1.000000e+01 0.0 0.0 7.759661e-35 3.506024e-63 4.150609e-24 0.0 0.0 1.574924e-24 8.431027e-25 ... -1.219336e-34 0.0 3.821641e-21 0.0 0.0 1.880749e-43 3.031307e-24 7.000000e-40 -8.135891e-26 0.0
9 1.000000e+02 0.0 0.0 1.055043e-34 1.601465e-62 3.574683e-23 0.0 0.0 2.009385e-23 9.058431e-25 ... -3.175190e-34 0.0 4.492683e-21 0.0 0.0 1.383387e-42 3.039901e-24 7.000000e-40 -1.244761e-24 0.0
10 2.000000e+02 0.0 0.0 1.400875e-34 6.085257e-62 1.135321e-22 0.0 0.0 7.100815e-23 5.470146e-24 ... -7.343722e-34 0.0 5.138866e-21 0.0 0.0 1.101764e-42 3.049794e-24 7.000000e-40 -3.113675e-24 0.0
11 3.000000e+02 0.0 0.0 1.757610e-34 1.811043e-61 3.515446e-22 0.0 0.0 2.358660e-22 4.441004e-23 ... -1.443247e-33 0.0 5.695782e-21 0.0 0.0 9.324994e-43 3.060225e-24 7.000000e-40 -5.500991e-24 0.0
12 4.000000e+02 0.0 0.0 2.105001e-34 4.446628e-61 7.458767e-22 0.0 0.0 4.532101e-22 1.789472e-22 ... -2.496413e-33 0.0 6.163665e-21 0.0 0.0 8.203232e-43 3.071104e-24 7.000000e-40 -8.215717e-24 0.0
13 5.000000e+02 0.0 0.0 2.407529e-34 8.920321e-61 1.306477e-21 0.0 0.0 7.266848e-22 4.213907e-22 ... -3.793595e-33 0.0 6.523852e-21 0.0 0.0 7.333908e-43 3.081609e-24 7.000000e-40 -1.087112e-23 0.0
14 6.000000e+02 0.0 0.0 2.661447e-34 1.556181e-60 2.158357e-21 0.0 0.0 1.067836e-21 8.894247e-22 ... -5.270724e-33 0.0 6.803286e-21 0.0 0.0 6.677252e-43 3.092336e-24 7.000000e-40 -1.338619e-23 0.0
15 7.000000e+02 0.0 0.0 2.854768e-34 2.397368e-60 3.159351e-21 0.0 0.0 1.384716e-21 1.530278e-21 ... -6.769924e-33 0.0 7.009663e-21 0.0 0.0 6.179980e-43 3.103177e-24 7.000000e-40 -1.560455e-23 0.0
16 8.000000e+02 0.0 0.0 2.978954e-34 3.301719e-60 4.100755e-21 0.0 0.0 1.647955e-21 2.169177e-21 ... -8.107446e-33 0.0 7.151321e-21 0.0 0.0 5.800996e-43 3.114127e-24 7.000000e-40 -1.740943e-23 0.0
17 9.000000e+02 0.0 0.0 3.044740e-34 4.146650e-60 4.851508e-21 0.0 0.0 1.848526e-21 2.687133e-21 ... -9.178149e-33 0.0 7.242572e-21 0.0 0.0 5.504575e-43 3.124652e-24 7.000000e-40 -1.878358e-23 0.0
18 1.000000e+03 0.0 0.0 3.067780e-34 4.905845e-60 5.435084e-21 0.0 0.0 1.996990e-21 3.095256e-21 ... -1.001629e-32 0.0 7.301227e-21 0.0 0.0 5.272374e-43 3.135140e-24 7.000000e-40 -1.984741e-23 0.0
19 2.000000e+03 0.0 0.0 2.467887e-34 7.783575e-60 7.158947e-21 0.0 0.0 2.241113e-21 4.432453e-21 ... -1.141973e-32 0.0 7.266573e-21 0.0 0.0 6.011086e-43 3.236002e-24 7.000000e-40 -2.299937e-23 0.0
20 3.000000e+03 0.0 0.0 1.820954e-34 8.009917e-60 6.976409e-21 0.0 0.0 1.989355e-21 4.428172e-21 ... -1.009954e-32 0.0 7.026947e-21 0.0 0.0 4.493836e-43 3.334018e-24 7.000000e-40 -2.320682e-23 0.0
21 4.000000e+03 0.0 0.0 1.372166e-34 8.246560e-60 6.741901e-21 0.0 0.0 1.737476e-21 4.386935e-21 ... -9.017462e-33 0.0 6.808183e-21 0.0 0.0 3.866661e-43 3.423660e-24 7.000000e-40 -2.341900e-23 0.0
22 5.000000e+03 0.0 0.0 9.670979e-35 8.646406e-60 6.471724e-21 0.0 0.0 1.433629e-21 4.354515e-21 ... -7.879750e-33 0.0 6.552151e-21 0.0 0.0 3.506612e-43 3.530211e-24 7.000000e-40 -2.376720e-23 0.0
23 6.000000e+03 0.0 0.0 7.343128e-35 9.009782e-60 6.282857e-21 0.0 0.0 1.215406e-21 4.337732e-21 ... -7.097726e-33 0.0 6.364602e-21 0.0 0.0 3.247799e-43 3.608807e-24 7.000000e-40 -2.407316e-23 0.0
24 7.000000e+03 0.0 0.0 5.236676e-35 9.366566e-60 6.087725e-21 0.0 0.0 9.824387e-22 4.327748e-21 ... -6.206339e-33 0.0 6.152176e-21 0.0 0.0 3.077879e-43 3.696188e-24 7.000000e-40 -2.436455e-23 0.0
25 8.000000e+03 0.0 0.0 3.483939e-35 9.690435e-60 5.873014e-21 0.0 0.0 7.596486e-22 4.288640e-21 ... -5.245737e-33 0.0 5.927502e-21 0.0 0.0 2.960742e-43 3.787153e-24 7.000000e-40 -2.462183e-23 0.0
26 9.000000e+03 0.0 0.0 2.364401e-35 9.907898e-60 5.693158e-21 0.0 0.0 6.021007e-22 4.232427e-21 ... -4.448098e-33 0.0 5.747900e-21 0.0 0.0 2.877779e-43 3.858925e-24 7.000000e-40 -2.479093e-23 0.0
27 1.000000e+04 0.0 0.0 1.453536e-35 1.012338e-59 5.505157e-21 0.0 0.0 4.645704e-22 4.151556e-21 ... -3.604903e-33 0.0 5.569542e-21 0.0 0.0 2.820920e-43 3.929932e-24 7.000000e-40 -2.495572e-23 0.0
28 2.000000e+04 0.0 0.0 4.204058e-39 9.139427e-60 3.878880e-21 0.0 0.0 1.305229e-23 2.890876e-21 ... -8.653598e-35 0.0 3.856126e-21 0.0 0.0 2.441808e-43 4.575572e-24 7.000000e-40 -2.418004e-23 0.0
29 3.000000e+04 0.0 0.0 3.484861e-39 6.895284e-60 2.675511e-21 0.0 0.0 4.759183e-24 1.749193e-21 ... -7.126339e-35 0.0 2.638103e-21 0.0 0.0 2.469465e-43 5.025666e-24 7.000000e-40 -2.213788e-23 0.0
30 4.000000e+04 0.0 0.0 3.234427e-39 4.832719e-60 1.832680e-21 0.0 0.0 2.572568e-24 1.021949e-21 ... -5.998565e-35 0.0 1.795690e-21 0.0 0.0 2.673782e-43 5.338339e-24 7.000000e-40 -1.975061e-23 0.0
31 5.000000e+04 0.0 0.0 3.040223e-39 3.376957e-60 1.272224e-21 0.0 0.0 1.534769e-24 5.831385e-22 ... -5.067806e-35 0.0 1.223275e-21 0.0 0.0 2.890581e-43 5.554960e-24 7.000000e-40 -1.754193e-23 0.0
32 6.000000e+04 0.0 0.0 2.790441e-39 2.149618e-60 8.669902e-22 0.0 0.0 9.213588e-25 3.233932e-22 ... -4.078814e-35 0.0 8.210216e-22 0.0 0.0 3.060460e-43 5.708481e-24 7.000000e-40 -1.502153e-23 0.0
33 7.000000e+04 0.0 0.0 2.516961e-39 1.373554e-60 5.982514e-22 0.0 0.0 5.672829e-25 1.734893e-22 ... -3.258633e-35 0.0 5.519997e-22 0.0 0.0 3.211900e-43 5.814173e-24 7.000000e-40 -1.278964e-23 0.0
34 8.000000e+04 0.0 0.0 2.242211e-39 8.855227e-61 4.042568e-22 0.0 0.0 3.495755e-25 7.468949e-23 ... -2.595692e-35 0.0 3.679853e-22 0.0 0.0 3.297568e-43 5.888991e-24 7.000000e-40 -1.084044e-23 0.0
35 9.000000e+04 0.0 0.0 1.995400e-39 5.752553e-61 2.916422e-22 0.0 0.0 2.215726e-25 3.747202e-23 ... -2.070925e-35 0.0 2.459149e-22 0.0 0.0 3.314728e-43 5.940267e-24 7.000000e-40 -9.137865e-24 0.0
36 1.000000e+05 0.0 0.0 1.718757e-39 3.377529e-61 2.064114e-22 0.0 0.0 1.316863e-25 2.313840e-23 ... -1.561223e-35 0.0 1.638731e-22 0.0 0.0 3.280947e-43 5.975159e-24 7.000000e-40 -7.304371e-24 0.0
37 2.000000e+05 0.0 0.0 8.999688e-40 1.201838e-62 1.454990e-23 0.0 0.0 7.415171e-27 9.396016e-26 ... -2.845361e-36 0.0 3.770210e-24 0.0 0.0 5.099910e-44 6.052441e-24 7.000000e-40 -9.600920e-25 0.0
38 3.000000e+05 0.0 0.0 5.359658e-40 6.879358e-63 6.778661e-24 0.0 0.0 8.819628e-27 5.280131e-26 ... -1.786389e-36 0.0 7.076531e-25 0.0 0.0 1.438185e-44 6.054109e-24 7.000000e-40 -5.000709e-25 0.0
39 4.000000e+05 0.0 0.0 5.659641e-40 6.440042e-63 6.199081e-24 0.0 0.0 1.091460e-26 3.836571e-26 ... -1.772020e-36 0.0 5.561210e-25 0.0 0.0 1.476274e-44 6.054055e-24 7.000000e-40 -4.529640e-25 0.0
40 5.000000e+05 0.0 0.0 6.399018e-40 6.397197e-63 6.057769e-24 0.0 0.0 1.448423e-26 3.022693e-26 ... -1.857734e-36 0.0 5.326686e-25 0.0 0.0 1.606737e-44 6.053935e-24 7.000000e-40 -4.482787e-25 0.0
41 6.000000e+05 0.0 0.0 9.164150e-40 6.878532e-63 5.882461e-24 0.0 0.0 2.288786e-26 1.689517e-26 ... -2.223251e-36 0.0 5.313387e-25 0.0 0.0 1.519789e-44 6.053515e-24 7.000000e-40 -4.999839e-25 0.0
42 7.000000e+05 0.0 0.0 1.191312e-39 7.337220e-63 5.807951e-24 0.0 0.0 2.361776e-26 1.299020e-26 ... -2.546136e-36 0.0 5.351541e-25 0.0 0.0 1.320935e-44 6.053298e-24 7.000000e-40 -5.474699e-25 0.0
43 8.000000e+05 0.0 0.0 1.871863e-39 8.073298e-63 5.716142e-24 0.0 0.0 2.403425e-26 9.700345e-27 ... -3.188680e-36 0.0 5.418687e-25 0.0 0.0 1.124983e-44 6.053076e-24 7.000000e-40 -6.204216e-25 0.0
44 9.000000e+05 0.0 0.0 2.665854e-39 8.603579e-63 5.637955e-24 0.0 0.0 2.404294e-26 8.219155e-27 ... -3.779656e-36 0.0 5.467853e-25 0.0 0.0 1.035242e-44 6.052969e-24 7.000000e-40 -6.707517e-25 0.0

45 rows × 23 columns

Plotting Individual Heating and Cooling Contributions#

Let’s visualize the dominant heating and cooling processes over time.

fig, axes = plt.subplots(2, 1, figsize=(12, 10))

# Plot heating processes
time = physics["Time"]
for col in heating_cols:
    # Only plot processes with significant contribution
    max_val = heating[col].abs().max()
    if max_val > 1e-30:  # Filter out negligible contributions
        label = col.replace("_heating", "").replace("_", " ")
        axes[0].plot(time, heating[col], label=label, linewidth=2, alpha=0.7)

axes[0].set_xlabel("Time (years)", fontsize=12)
axes[0].set_ylabel("Heating Rate (erg cm⁻³ s⁻¹)", fontsize=12)
axes[0].set_xscale("log")
axes[0].set_yscale("symlog", linthresh=1e-30)
axes[0].set_title("Heating Processes", fontsize=14)
axes[0].legend(fontsize=8, loc="best")
axes[0].grid(True, alpha=0.3)

# Plot cooling processes (select major ones to avoid cluttering)
for col in cooling_cols:
    max_val = heating[col].abs().max()
    if max_val > 1e-40:
        label = col.replace("_cooling", "").replace("_", " ")
        axes[1].plot(time, heating[col], label=label, linewidth=2, alpha=0.7)

axes[1].set_xlabel("Time (years)", fontsize=12)
axes[1].set_ylabel("Cooling Rate (erg cm⁻³ s⁻¹)", fontsize=12)
axes[1].set_xscale("log")
axes[1].set_yscale("symlog", linthresh=1e-30)
axes[1].set_title("Major Cooling Processes", fontsize=14)
axes[1].legend(fontsize=8, loc="best")
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()
../_images/6aca8aec4115bb97f71b1017834a3bbfa66eef4fab029c93cafccfa497f8145a.png

Example 4: Writing Heating Rates to a File#

You can also save heating and cooling rates directly to a file during the model run using the heatingFile parameter. This requires not running any other cells to memory before!

# You can uncomment the cel below to run a model to disk.

# # Run model with heating file output
# param_dict_with_file = param_dict_heating.copy()
# param_dict_with_file["outputFile"] = "../examples/test-output/heating_demo.dat"
# param_dict_with_file["heatingFile"] = "../examples/test-output/heating_rates.dat"


# result = uclchem.model.cloud(
#     param_dict=param_dict_with_file, out_species=["CO", "H2O", "OH"]
# )

# print(f"Model completed with flag: {result[0]}")
# print(f"Final CO abundance: {result[1]:.2e}")
# print(f"Final H2O abundance: {result[2]:.2e}")
# print(f"Final OH abundance: {result[3]:.2e}")

# # Read the heating file
# if os.path.exists("../examples/test-output/heating_rates.dat"):
#     print("\nHeating rates file created successfully!")
#     heating_df = pd.read_csv("../examples/test-output/heating_rates.dat")
#     print(f"File contains {len(heating_df)} time steps")
#     print(f"Columns: {heating_df.columns.tolist()[:10]}...")  # Show first 10 columns

Example 5: Different Physical Conditions#

Let’s explore how heating and cooling behave under different physical conditions. We’ll compare a dense, cold core with a less dense, warmer environment.

# Dense, cold core (like Example 1)
param_cold_core = {
    "initialDens": 1e5,
    "initialTemp": 10.0,
    "finalTime": 1.0e6,
    "baseAv": 20.0,
    "rout": 0.05,
    "freefall": False,
    "heatingFlag": True,
}

# Less dense, warmer cloud
param_warm_cloud = {
    "initialDens": 1e3,
    "initialTemp": 30.0,
    "finalTime": 1.0e6,
    "baseAv": 5.0,
    "rout": 0.5,
    "freefall": False,
    "heatingFlag": True,
}

# Run both models
print("Running cold core model...")
cloud_cold = uclchem.model.Cloud(param_dict=param_cold_core)
phys_cold, abund_cold = cloud_cold.get_dataframes(joined=False)
_, _, rates_cold = cloud_cold.get_dataframes(joined=False, with_rates=True)
_, _, heating_cold = cloud_cold.get_dataframes(joined=False, with_heating=True)
flag_cold = 0 if cloud_cold.has_attr("_data") else -1

print("Running warm cloud model...")
cloud_warm = uclchem.model.Cloud(param_dict=param_warm_cloud)
phys_warm, abund_warm = cloud_warm.get_dataframes(joined=False)
_, _, rates_warm = cloud_warm.get_dataframes(joined=False, with_rates=True)
_, _, heating_warm = cloud_warm.get_dataframes(joined=False, with_heating=True)
flag_warm = 0 if cloud_warm.has_attr("_data") else -1

print(f"\nCold core flag: {flag_cold}, Warm cloud flag: {flag_warm}")

# Compare temperature evolution
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

axes[0].plot(
    phys_cold["Time"],
    phys_cold["gasTemp"],
    label=f"Dense Core (n={param_cold_core['initialDens']:.0e})",
    linewidth=2,
)
axes[0].plot(
    phys_warm["Time"],
    phys_warm["gasTemp"],
    label=f"Diffuse Cloud (n={param_warm_cloud['initialDens']:.0e})",
    linewidth=2,
)
axes[0].set_xlabel("Time (years)", fontsize=12)
axes[0].set_ylabel("Gas Temperature (K)", fontsize=12)
axes[0].set_xscale("log")
axes[0].set_title("Temperature Evolution", fontsize=14)
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# Compare CO abundance
axes[1].plot(phys_cold["Time"], abund_cold["CO"], label="Dense Core", linewidth=2)
axes[1].plot(phys_warm["Time"], abund_warm["CO"], label="Diffuse Cloud", linewidth=2)
axes[1].set_xlabel("Time (years)", fontsize=12)
axes[1].set_ylabel("CO Abundance", fontsize=12)
axes[1].set_xscale("log")
axes[1].set_yscale("log")
axes[1].set_title("CO Abundance", fontsize=14)
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("\nFinal temperatures:")
print(f"  Cold core: {phys_cold['gasTemp'].iloc[-1]:.2f} K")
print(f"  Warm cloud: {phys_warm['gasTemp'].iloc[-1]:.2f} K")
Running cold core model...
Running warm cloud model...

Cold core flag: -1, Warm cloud flag: -1

Final temperatures:
  Cold core: 11.06 K
  Warm cloud: 20.32 K
../_images/f071f67c3370bc36e2ae7e2e9008c67efcecac635e42a7533bd08d8437bbd2e9.png

Summary of Key Heating and Cooling Processes#

UCLCHEM includes the following heating and cooling mechanisms:

Major Heating Processes:#

  1. Photoelectric heating - from PAHs and dust grains (Bakes & Tielens 1994)

  2. H₂ formation heating - energy released during H₂ formation on grains

  3. H₂ photodissociation heating - kinetic energy from photodissociation

  4. Cosmic ray heating - direct and indirect (via ionization)

  5. Chemical reaction enthalpy - exothermic reactions (optional, requires network configuration)

  6. X-ray heating - in environments with X-ray sources

  7. Turbulent heating - dissipation of turbulent energy

  8. Viscous heating - for shock models

Major Cooling Processes:#

  1. Molecular line cooling - CO, H₂O, OH, and other molecules

  2. Atomic fine structure cooling - C⁺, O, C, etc.

  3. Dust continuum cooling - gas-grain collisional cooling

  4. H₂ line cooling - rovibrational transitions

  5. Recombination cooling - electronic to kinetic energy

  6. Ly-α cooling - Lyman-alpha photon emission

Key Parameters:#

  • heatingFlag: Enable/disable all heating processes (default: True)

  • heatingFile: Path to write detailed heating/cooling rates

  • initialTemp: Starting gas temperature

  • baseAv: Visual extinction (affects radiation field attenuation)

  • zeta: Cosmic ray ionization rate

For a complete list of processes and their equations, see the HEATING_COOLING_SUMMARY.md documentation file.

Advanced: Chemical Reaction Enthalpy#

UCLCHEM can optionally include the enthalpy changes from chemical reactions as a heating/cooling source. This requires configuring the chemical network at build time using MakeRates.

Note: This feature requires rebuilding UCLCHEM with specific settings in the user_settings.yaml file. The main settings are:

  • add_delta_enthalpy: Can be False, GAS, or ALL

    • False: No reaction enthalpy (default)

    • GAS: Include enthalpy from gas-phase reactions only

    • ALL: Include enthalpy from all reactions (gas + surface)

To enable reaction enthalpy:

# In Makerates/user_settings.yaml
add_delta_enthalpy: GAS

Then rebuild:

cd Makerates
python MakeRates.py
cd ..
pip install .

For more details, see the heating_cooling_benchmarks/ directory which contains examples with different enthalpy configurations.

Tips for Using Heating and Cooling#

  1. Always check convergence: Use return_dataframe=True and inspect temperature evolution to ensure physical results

  2. Monitor element conservation: Use uclchem.analysis.check_element_conservation() to verify model accuracy

  3. Save heating rates for analysis: Use heatingFile parameter to save detailed heating/cooling contributions

  4. Choose appropriate tolerances: If you see unphysical temperature spikes, try adjusting reltol and abstol parameters

  5. Consider your environment: Different astrophysical environments have different dominant heating/cooling processes:

    • Dense cores: Line cooling (CO, H₂O) dominates

    • Diffuse clouds: Photoelectric heating and fine-structure cooling

    • Shock regions: Viscous/turbulent heating important

    • PDRs: Strong FUV field means photoelectric heating dominates

  6. Radiation field matters: The baseAv parameter controls UV attenuation, which significantly affects photoelectric heating and photodissociation rates

Next Steps#

  • Check out 7_heating_cooling_settings.ipynb for more advanced configuration options

  • See HEATING_COOLING_SUMMARY.md for detailed equations and references

  • Explore the heating_cooling_benchmarks/ directory for systematic comparisons