<jupyter_start><jupyter_text>Ionization of Carbon and OxygenFigure 5.6 from Chapter 5 of *Interstellar and Intergalactic Medium* by <NAME>, 2021, Cambridge University Press.Calculate and plot the ion fractions for carbon and oxygen in collisional ionization equilibrium (CIE) fortemperatures ranging from 104 to 107K.  Uses data from the[CHIANTI atomic database](https://www.chiantidatabase.org/) and the [ChiantiPy](https://github.com/chianti-atomic/ChiantiPy/) module perform the CIE calculations. Alternative VersionThis notebook offers an alternative version of Fig5_6_IonFrac.ipynb that performs the ChiantiPycalculations in-situ rather than reading pre-calculated data from external files. This notebook maybe used, for example, to explore other species (e.g., N or Fe).You will need to install the latest versions of ChiantiPy and the CHIANTI atomic database - available separately -before you can use this notebook, and the versions of each must be compatible. At the time of this notebook'srelease, we tested it with ChiantiPy version 0.9.5 and CHIANTI version 10.0. See the caveat below. CaveatChiantiPy and CHIANTI must both be installed on your system and uptodate to run this notebook. Exceptionsraised because of missing files are often due to having updated ChiantiPy but not the full CHIANTI database.Being diligent about keeping the two in-sync is part of using Chianti. CHIANTI citationCHIANTI is a collaborative project involving George Mason University, the University of Michigan (USA),University of Cambridge (UK) and NASA Goddard Space Flight Center (USA).<jupyter_code>%matplotlib inline

import math
import numpy as np

import matplotlib
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, LogLocator, NullFormatter

# Throttle nuisance warnings

import warnings
warnings.filterwarnings('ignore',category=UserWarning, append=True)

# Chianti CIE code 

import ChiantiPy.core as ch<jupyter_output><empty_output><jupyter_text>Useful Functions<jupyter_code># convert integer to roman numerals

def int2roman(integer):
    table = [['M',1000],['CM',900],['D',500],['CD',400],['C',100],
             ['XC',90],['L',50],['XL',40],['X',10],['IX',9],
             ['V',5],['IV',4],['I',1]]
    parts = []
    for letter, value in table:
        while value <= integer:
            integer -= value
            parts.append(letter)
    return ''.join(parts)

species = ['H','He','Li','Be','B','C','N','O','F','Ne','Na','Mg','Al',
           'Si','P','S','Cl','Ar','K','Ca','Sc','Ti','V','Cr','Mn','Fe',
           'Co','Ni']<jupyter_output><empty_output><jupyter_text>Standard Plot FormatSetup the standard plotting format and make the plot. Fonts and resolution adopted follow CUP style.<jupyter_code>figName = 'ionFrac_CO' 

# graphic aspect ratio = width/height

aspect = 2.5

# Text width in inches - don't change, this is defined by the print layout

textWidth = 6.0 # inches

# output format and resolution

figFmt = 'png'
dpi = 600

# Graphic dimensions 

plotWidth = dpi*textWidth
plotHeight = plotWidth/aspect
axisFontSize = 10
labelFontSize = 6
lwidth = 0.5
axisPad = 5
wInches = textWidth 
hInches = wInches/aspect

# Plot filename

plotFile = f'{figName}.{figFmt}'

# LaTeX is used throughout for markup of symbols, Times-Roman serif font

plt.rc('text', usetex=True)
plt.rc('font', **{'family':'serif','serif':['Times-Roman'],'weight':'bold','size':'16'})

# Font and line weight defaults for axes

matplotlib.rc('axes',linewidth=lwidth)
matplotlib.rcParams.update({'font.size':axisFontSize})

# axis and label padding

plt.rcParams['xtick.major.pad'] = f'{axisPad}'
plt.rcParams['ytick.major.pad'] = f'{axisPad}'
plt.rcParams['axes.labelpad'] = f'{axisPad}'<jupyter_output><empty_output><jupyter_text>Ionization Fractions of Carbon & OxygenCarbon has nuclear charge Z=6, Oxygen is Z=8.<jupyter_code># Range of temperatures

minT = 9000. # K
maxT = 1.0e7 # K

minLogT = math.log10(minT)
maxLogT = math.log10(maxT)
dlogT = 0.01
numT = 1+int((maxLogT-minLogT)/dlogT)
logT = np.linspace(minLogT,maxLogT,num=numT)

T = 10.0**logT # temperature (K)

# Calculate CIE ion fractions of Carbon

zC = 6
cieC = ch.ioneq(zC)
cieC.load('chianti')  # default CHIANTI database
cieC.calculate(T)

# Calculate CIE ion fractions of Oxygen

zO = 8

cieO = ch.ioneq(zO)  
cieO.load('chianti')  # default CHIANTI database
cieO.calculate(T)<jupyter_output><empty_output><jupyter_text>2-Panel side-by-side plotPlot C and O in side-by-side panels, same X and Y axes, thin space between them.<jupyter_code>fig,ax = plt.subplots()

fig.set_dpi(dpi)
fig.set_size_inches(wInches,hInches,forward=True)

fig.subplots_adjust(wspace=0.1, hspace=0)

# Left Panel: Carbon

ax1 = plt.subplot(121)

ax1.set_xlim(minT,maxT)
ax1.set_xscale('log')
ax1.set_xticks([1e4,1e5,1e6,1e7])
locmin = matplotlib.ticker.LogLocator(base=10.0,subs=[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9],numticks=10)
ax1.xaxis.set_minor_locator(locmin)
ax1.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())

ax1.set_ylim(0.0,1.1)

ax1.tick_params('both',length=4,width=lwidth,which='major',direction='in',top='on',right='on')
ax1.tick_params('both',length=2,width=lwidth,which='minor',direction='in',top='on',right='on')

ax1.yaxis.set_major_locator(MultipleLocator(0.2))
ax1.yaxis.set_minor_locator(MultipleLocator(0.05))

plt.xlabel(r'Temperature [K]',fontsize=axisFontSize)
plt.ylabel(r'Ion fraction',fontsize=axisFontSize)

stage = 0
for ionfrac in cieC.Ioneq:
    plt.plot(cieC.Temperature,ionfrac,color='black',ls='-',lw=1.0,zorder=10)
    stage += 1
    if stage <= zC+1:
        maxfrac = np.max(ionfrac)+0.01
        imax = np.argmax(ionfrac)
        if stage == 1:
            plt.text(11000.,0.97*maxfrac,int2roman(stage),fontsize=labelFontSize)
        elif stage == 4:
            plt.text(T[imax]+20000.,0.8*maxfrac,int2roman(stage),fontsize=labelFontSize)
        elif stage == 7:
            plt.text(6e6,maxfrac,int2roman(stage),ha='center',fontsize=labelFontSize)
        else: 
            plt.text(T[imax],maxfrac,int2roman(stage),fontsize=labelFontSize,ha='center')
            
plt.title('Carbon',fontsize=axisFontSize)  

# Right Panel: Oxygen

ax2 = plt.subplot(122)

ax2.set_xlim(minT,maxT)
ax2.set_xscale('log')
ax2.set_xticks([1e4,1e5,1e6,1e7])
ax2.xaxis.set_minor_locator(locmin)
ax2.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())

ax2.set_ylim(0.0,1.1)
ax2.set_yticklabels([]) # suppress Y-axis labels

ax2.tick_params('both',length=4,width=lwidth,which='major',direction='in',top='on',right='on')
ax2.tick_params('both',length=2,width=lwidth,which='minor',direction='in',top='on',right='on')

ax2.yaxis.set_major_locator(MultipleLocator(0.2))
ax2.yaxis.set_minor_locator(MultipleLocator(0.05))

plt.xlabel(r'Temperature [K]',fontsize=axisFontSize)

stage = 0
for ionfrac in cieO.Ioneq:
    plt.plot(cieO.Temperature,ionfrac,color='black',ls='-',lw=1.0,zorder=10)
    stage += 1
    if stage <= zO+1:
        maxfrac = np.max(ionfrac)+0.01
        imax = np.argmax(ionfrac)
        if stage == 1:
            plt.text(10500.,maxfrac,int2roman(stage),fontsize=labelFontSize,ha='center')
        elif stage == 8:
            plt.text(1.06*T[imax],maxfrac,int2roman(stage),rotation=90.,fontsize=labelFontSize,
                     ha='center',va='bottom')
        elif stage == 6:
            plt.text(3.5e5,0.75*maxfrac,int2roman(stage),fontsize=labelFontSize)
        elif stage == 9:
            plt.text(7e6,maxfrac,int2roman(stage),ha='center',fontsize=labelFontSize)
        else: 
            plt.text(T[imax],maxfrac,int2roman(stage),fontsize=labelFontSize,ha='center')
            
plt.title('Oxygen',fontsize=axisFontSize)  

plt.plot()
plt.savefig(plotFile,bbox_inches='tight',facecolor='white')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14100090055319697, 'fraction_numerical': 0.032419915090698574, 'mean_word_length': 5.0077279752704795, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 22, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '270642', 'license': 'CC0-1.0', 'n_tokens_mistral': 2967, 'n_tokens_neox': 2799, 'n_words': 575, 'path': 'Chapter5/ionFrac_Calc.ipynb', 'repo_name': 'CambridgeUniversityPress/Interstellar-and-Intergalactic-Medium', 'size': '7780'}