<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.Plot of the ion fractions for carbon and oxygen in collisional ionization equilibrium (CIE) as a functionof temperature from 104 to 107K.  Uses precomputed ion fractions derived using[ChiantiPy](https://github.com/chianti-atomic/ChiantiPy/) and the [CHIANTI atomic database](https://www.chiantidatabase.org/).To explore other elements, see the **ionFrac_Calc.ipynb** notebook that does the calculations withChiantiPy in-situ.CHIANTI 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 pandas as pd

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)<jupyter_output><empty_output><jupyter_text>Special Functionsint2roman() converts an integer to roman numerals (string).<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)<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 = 'Fig5_6' 

# 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 & OxygenRead in the ionization fractions of C and O from pre-computed multi-column ASCII text files: * ionFrac_C.txt - Carbon * ionFrac_O.txt - Oxygen<jupyter_code># carbon

cFile = 'ionFrac_C.txt'
zC = 6  # nuclear charge

cFrac = {}
data = pd.read_csv(cFile,sep=r'\s+',comment='#')

cTemp = 10.0**np.array(data['logT'])
for i in range(zC+1):
    stage = int2roman(i+1)
    cFrac[stage] = np.array(data[stage])

# oxygen

oFile = 'ionFrac_O.txt'
zO = 8  # nuclear charge

oFrac = {}
data = pd.read_csv(oFile,sep=r'\s+',comment='#')

oTemp = 10.0**np.array(data['logT'])
for i in range(zO+1):
    stage = int2roman(i+1)
    oFrac[stage] = np.array(data[stage])

# axis limits

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

minY = 0.0
maxY = 1.1<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(minY,maxY)

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)

for i in range(zC+1):
    stage = int2roman(i+1)
    plt.plot(cTemp,cFrac[stage],color='black',ls='-',lw=1.0,zorder=10)
    if i <= zC:
        maxfrac = np.max(cFrac[stage])+0.01
        imax = np.argmax(cFrac[stage])
        if i == 0:
            plt.text(11000.,0.97*maxfrac,stage,fontsize=labelFontSize)
        elif i == 3:
            plt.text(cTemp[imax]+20000.,0.8*maxfrac,stage,fontsize=labelFontSize)
        elif i == 6:
            plt.text(6e6,maxfrac,stage,ha='center',fontsize=labelFontSize)
        else: 
            plt.text(cTemp[imax],maxfrac,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(minY,maxY)
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)

for i in range(zO+1):
    stage = int2roman(i+1)
    plt.plot(oTemp,oFrac[stage],color='black',ls='-',lw=1.0,zorder=10)
    if i <= zO:
        maxfrac = np.max(oFrac[stage])+0.01
        imax = np.argmax(oFrac[stage])
        if i == 0:
            plt.text(10500.,maxfrac,stage,fontsize=labelFontSize,ha='center')
        elif i == 7:
            plt.text(1.06*oTemp[imax],maxfrac,stage,rotation=90.,fontsize=labelFontSize,
                     ha='center',va='bottom')
        elif i == 5:
            plt.text(3.5e5,0.75*maxfrac,stage,fontsize=labelFontSize)
        elif i == 8:
            plt.text(7e6,maxfrac,stage,ha='center',fontsize=labelFontSize)
        else: 
            plt.text(oTemp[imax],maxfrac,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.15123231733994458, 'fraction_numerical': 0.03397987458072043, 'mean_word_length': 4.937662337662338, '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': '569364', 'license': 'CC0-1.0', 'n_tokens_mistral': 2721, 'n_tokens_neox': 2573, 'n_words': 458, 'path': 'Chapter5/Fig5_6_IonFrac.ipynb', 'repo_name': 'CambridgeUniversityPress/Interstellar-and-Intergalactic-Medium', 'size': '6864'}