# NOTE: For some of the vapour pressure values, you need to perform a boiling point estimation first
# It is therefore wise to do this initially

# 2a) Boiling points [(K)]

boiling_point_dict = collections.defaultdict(lambda: collections.defaultdict())

for smiles in smiles_array:

    boiling_point_dict[smiles][
        'joback_and_reid'] = boiling_points.joback_and_reid(
            Pybel_object_dict[smiles])
    boiling_point_dict[smiles][
        'stein_and_brown'] = boiling_points.stein_and_brown(
            Pybel_object_dict[smiles])
    boiling_point_dict[smiles]['nannoolal'] = boiling_points.nannoolal(
        Pybel_object_dict[smiles])

# 2b) Vapour pressures [log10 (atm) at a specific temperature]
# For those vapour pressure methods that require a boiling point, we have 3D dictionaries

vapour_pressure_dict_BP = collections.defaultdict(
    lambda: collections.defaultdict(lambda: collections.defaultdict()))
vapour_pressure_dict = collections.defaultdict(
    lambda: collections.defaultdict())
temperature = 298.15

for smiles in smiles_array:

    vapour_pressure_dict_BP[smiles]['VP_Nannoolal'][
        'BP_Nannoolal'] = vapour_pressures.nannoolal(
            Pybel_object_dict[smiles], temperature,
Ejemplo n.º 2
0
def Pure_component1(num_species, species_dict, species_dict2array,
                    Pybel_object_dict, SMILES_dict, temp, vp_method, bp_method,
                    critical_method, density_method, ignore_vp, vp_cutoff):
    """ This function calculates properties that dictate gas-to-particle partitioning

    inputs:
    • num_species - number of compounds used in the calculations
    • species_dict - dict holding names of all compounds
    • species_dict2array - dict that mapes compound name to array index
    • Pybel_object_dict - dict holding all Pybel objects of each compound [used in UManSysProp]
    • SMILES_dict - dict containing all SMILES strings of each compound
    • temp - temperature [K]
    • vp_method - choice of vapour pressure method [see calling function and/or UManSysProp]
    • bp_method - choice of boiling point method [see calling function and/or UManSysProp]
    • critical_method - choice of criticl property method [see calling function and/or UManSysProp]
    • density_method - choice of density method [see calling function and/or UManSysProp]
    • ignore_vp - flag to ignore compounds with vapour pressure above a defined value given by 'vp_cutoff'
    • vp_cutoff - log10 value of vapour pressure above which the odel will ignore partitioning
    outputs:
    • return_dict['y_density_array']=y_density_array - density of each condensing compound
    • return_dict['y_mw']=y_mw - molecular weight of each condensing compound
    • return_dict['sat_vp']=sat_vp - saturation vapour pressure of each condensing compound
    • return_dict['Delta_H']=Delta_H - enthalpy of vapourisation of each condensing compound
    • return_dict['Latent_heat_gas']=Latent_heat_gas - latent hear of condensation of each condensing compound   
    • return_dict['ignore_index']=ignore_index - array that holds information on which compounds to ignore for partitioning
    • return_dict['ignore_index_fortran']=ignore_index_fortran - dense array that holds information on which compounds to ignore for partitioning in Fortran functions
    • return_dict['include_index']=include_index - array that holds information on which compounds to include for partitioning
  
    """

    y_density_array = [1000.0] * num_species
    y_mw = [200.0] * num_species
    o_c = [0.0] * num_species
    h_c = [0.0] * num_species
    sat_vp = [100.0] * num_species
    sat_vp_org = dict(
    )  #Recorded seperately and will not include the extension for water. This is used for any checks with equilibrium partitioning predictions
    y_gas = [0.0] * num_species
    species_step = 0
    ignore_index = [
    ]  #Append to this to identify any compounds that do not have automated calculation of properties. OR are species that will be ignored in partitioning
    include_index = []
    include_dict = dict()
    ignore_index_fortran = numpy.zeros((num_species), )
    print("Calculating component properties using UManSysProp")

    # Which boiling point method has been chosen
    boiling_point = {
        'joback_and_reid': boiling_points.joback_and_reid,
        'stein_and_brown': boiling_points.stein_and_brown,
        'nannoolal': boiling_points.nannoolal,
    }[bp_method]
    vapour_pressure = {
        'nannoolal': vapour_pressures.nannoolal,
        'myrdal_and_yalkowsky': vapour_pressures.myrdal_and_yalkowsky,
        # Evaporation doesn't use boiling point
        'evaporation': lambda c, t, b: vapour_pressures.evaporation(c, t),
    }[vp_method]
    # Which density method has been chosen
    critical_property = {
        'nannoolal': critical_properties.nannoolal,
        'joback_and_reid': critical_properties.joback_and_reid,
    }[critical_method]
    liquid_density = {
        'girolami': lambda c, t, p: liquid_densities.girolami(c),
        'schroeder': liquid_densities.schroeder,
        'le_bas': liquid_densities.le_bas,
        'tyn_and_calus': liquid_densities.tyn_and_calus,
    }[density_method]

    for compound in species_dict.values():
        if compound in SMILES_dict.keys():

            # Calculate a boiling point with Nanoolal for density methods
            #pdb.set_trace()
            b1 = boiling_points.nannoolal(
                Pybel_object_dict[SMILES_dict[compound]])
            y_density_array[species_dict2array[compound]] = liquid_density(
                Pybel_object_dict[SMILES_dict[compound]], temp,
                critical_property(Pybel_object_dict[SMILES_dict[compound]],
                                  b1)) * 1.0E3

            #y_density_array[species_dict2array[compound]]=(liquid_densities.girolami(Pybel_object_dict[SMILES_dict[compound]])*1.0E3) #Convert from g/cc to kg/m3
            #y_density_array.append(1400.0)
            y_mw[species_dict2array[compound]] = (
                Pybel_object_dict[SMILES_dict[compound]].molwt)
            #In the following you will need to select which vapour pressure method you like.

            groups_dict = groups.composition(
                Pybel_object_dict[SMILES_dict[compound]])
            o_c[species_dict2array[
                compound]] = groups_dict['O'] / groups_dict['C']
            h_c[species_dict2array[
                compound]] = groups_dict['H'] / groups_dict['C']

            # Calculate boiling point
            b = boiling_point(Pybel_object_dict[SMILES_dict[compound]])

            sat_vp[species_dict2array[compound]] = vapour_pressure(
                Pybel_object_dict[SMILES_dict[compound]], temp, b)

            if ignore_vp is True:
                if sat_vp[species_dict2array[compound]] > vp_cutoff:
                    ignore_index.append(species_dict2array[compound])
                    ignore_index_fortran[species_dict2array[compound]] = 1.0
                else:
                    include_index.append(species_dict2array[compound])
            else:
                include_index.append(species_dict2array[compound])

            #sat_vp[species_dict2array[compound]]=(vapour_pressures.nannoolal(Pybel_object_dict[SMILES_dict[compound]], temp, boiling_points.nannoolal(Pybel_object_dict[SMILES_dict[compound]])))
            #sat_vp_org[Pybel_object_dict[SMILES_dict[compound]]]=vapour_pressures.nannoolal(Pybel_object_dict[SMILES_dict[compound]], temp, boiling_points.nannoolal(Pybel_object_dict[SMILES_dict[compound]]))
            #y_gas.append(concentration_array[species_step])
        else:
            ignore_index.append(species_dict2array[compound])
            ignore_index_fortran[species_dict2array[compound]] = 1.0
        species_step += 1
    Delta_H = [
        0.0
    ] * num_species  #Dont prescribe an enthalpy of vaporisation. For non-VBS model simulations with varying temperature, we recalculate using GCM
    Latent_heat_gas = [
        0.0
    ] * num_species  # - Still need to account for any latent heat release [Future work]

    return_dict = dict()
    return_dict['y_density_array'] = y_density_array
    return_dict['y_mw'] = y_mw
    return_dict['o_c'] = o_c
    return_dict['h_c'] = h_c
    return_dict['sat_vp'] = sat_vp
    return_dict['Delta_H'] = Delta_H
    return_dict['Latent_heat_gas'] = Latent_heat_gas
    return_dict['ignore_index'] = ignore_index
    return_dict['ignore_index_fortran'] = ignore_index_fortran
    return_dict['include_index'] = include_index

    return return_dict
Ejemplo n.º 3
0
	
from umansysprop import boiling_points
from umansysprop import vapour_pressures
from umansysprop import liquid_densities

NA = si.Avogadro # Avogadro's number (molecules/mol)
# vapour pressures of components, excluding water and core at end
Psat = np.zeros((1, num_comp-2))
TEMP = 298.15 # temperature (K) 


for i in range(num_comp-2): # component loop
	# vapour pressure (log10(atm)) (# eq. 6 of Nannoolal et al. (2008), with dB of 
	# that equation given by eq. 7 of same reference)
	Psat[0, i] = ((vapour_pressures.nannoolal(Pybel_objects[i], TEMP, 
			boiling_points.nannoolal(Pybel_objects[i]))))

# convert from list to array
y_mw = np.array((y_mw))

# convert vapour pressures in log10(atm) to saturation concentrations in ug/m3
# using eq. 1 of O'Meara et al. 2014
Psat = 1.e6*y_mw[0:-2].reshape(1, -1)*(10**(Psat))/(8.2057e-5*TEMP)

# convert particle-phase concentrations from molecules/cc to ug/m3 to 
# be comparable with total secondary particulate matter concentration
# isolate just particle-phase concentrations (molecules/cc)
# note just one size bin in this simulation and want to exclude water 
# and core
SPMi = y[:, num_comp:(num_comp*(num_sb)-2)]
# convert to mol/cc
Ejemplo n.º 4
0
def prop_calc(rel_SMILES, Pybel_objects, TEMP, H2Oi, num_comp, Psat_water,
              vol_Comp, volP, testf, corei, pconc, umansysprop_update,
              core_dens, spec_namelist, ode_gen_flag, nuci, nuc_comp, num_asb,
              dens_comp, dens, seed_name, y_mw):

    # inputs: ------------------------------------------------------------
    # rel_SMILES - array of SMILE strings for components
    # (omitting water and core, if present)
    # Pybel_objects - list of Pybel objects representing the components in rel_SMILES
    # (omitting water and core, if present)
    # TEMP - temperature (K) in chamber at time function called
    # vol_Comp - names of components (corresponding to those in chemical scheme file)
    # 			that have vapour pressures manually set in volP
    # testf - flag for whether in normal mode (0) or testing mode (1)
    # corei - index of seed particle component
    # pconc - initial number concentration of particles (# particles/cm3 (air))
    # umansysprop_update - marker for cloning UManSysProp so that latest version used
    # core_dens - density of core material (g/cm3 (liquid/solid density))
    # spec_namelist - list of component names in chemical equation file
    # ode_gen_flag - whether or not called from middle or ode_gen
    # nuci - index of nucleating component
    # nuc_comp - name of nucleating component
    # num_asb - number of actual size bins (excluding wall)
    # dens_comp - chemical scheme names of components with manually assigned
    # 	densities
    # dens - manually assigned densities (g/cm3)
    # seed_name - chemical scheme name(s) of component(s) comprising seed particles
    # y_mw - molar mass of components (g/mol)
    # ------------------------------------------------------------

    if (testf == 1):
        return (0, 0, 0)  # return dummies

    cwd = os.getcwd()  # address of current working directory

    # if update required, note this update flag is set when model variables are checked
    if (umansysprop_update == 1):
        # download latest version of umansysprop

        # check if there is an existing umansysprop folder
        if os.path.isdir(cwd + '/umansysprop'):

            def handleRemoveReadonly(func, path, exc):
                excvalue = exc[1]
                if not os.access(path, os.W_OK):
                    # Is the error an access error ?
                    os.chmod(path, stat.S_IWUSR)
                    func(path)
                else:
                    raise

            # remove existing folder, onerror will change permission of directory if
            # needed
            shutil.rmtree(cwd + '/umansysprop',
                          ignore_errors=False,
                          onerror=handleRemoveReadonly)

        git_url = 'https://github.com/loftytopping/UManSysProp_public.git'
        Repo.clone_from(git_url, (cwd + '/umansysprop'))

    # point to umansysprop folder
    sys.path.insert(1, (cwd + '/umansysprop'))  # address for updated version

    from umansysprop import boiling_points
    from umansysprop import vapour_pressures
    from umansysprop import liquid_densities

    NA = si.Avogadro  # Avogadro's number (molecules/mol)
    y_dens = np.zeros((num_comp, 1))  # components' liquid density (kg/m3)
    # vapour pressures of components, ensures any seed component called
    # core has zero vapour pressure
    Psat = np.zeros((1, num_comp))
    # oxygen:carbon ratio of components
    OC = np.zeros((1, num_comp))

    if (ode_gen_flag == 0):  # estimate densities if called from middle

        for i in range(num_comp):  # loop through components

            # density estimation ---------------------------------------------------------
            if (i == H2Oi):  # liquid-phase density of water
                y_dens[i] = 1. * 1.e3  # (kg/m3 (particle))
                continue
            # core component properties
            if (i == corei[0]):  # density of core
                y_dens[i] = core_dens * 1.e3  # core density (kg/m3 (particle))
                continue
            if rel_SMILES[
                    i] == '[HH]':  # omit H2 as unliked by liquid density code
                # liquid density code does not like H2, so manually input kg/m3
                y_dens[i] = 1.e3
            else:
                # density (convert from g/cm3 to kg/m3)
                y_dens[i] = liquid_densities.girolami(Pybel_objects[i]) * 1.e3
            # ----------------------------------------------------------------------------

    # account for any manually assigned component densities (kg/m3)
    if (len(dens_comp) > 0 and ode_gen_flag == 0):
        for i in range(len(dens_comp)):
            # index of component in list of components
            dens_indx = spec_namelist.index(dens_comp[i])
            y_dens[dens_indx] = dens[i]

    # for records (e.g. plotting volatility basis set),
    # estimate and list the pure component saturation vapour
    # pressures (Pa) at standard temperature (298.15 K), though note that
    # any manually assigned vapour pressures overwrite these later in
    # this module
    Psat_Pa_rec = np.zeros((num_comp))

    # estimate vapour pressures (log10(atm)) and O:C ratio
    # note when the O:C ratio and vapour pressure at 298.15 K are
    # combined, one can produce the two-dimensional volatility
    # basis set, as shown in Fig. 1 of https://doi.org/10.5194/acp-20-1183-2020
    for i in range(num_comp):

        if (i == corei[0]):  # if this component is 'core'
            # core component not included in Pybel_objects
            # assign an assumed O:C ratio of 0.
            OC[0, i] = 0.
            # continuing
            # here means its vapour pressure is 0 Pa, which is fine; if a
            # different vapour pressure is specified it is accounted for below
            continue

        # water vapour pressure already given by Psat_water (log10(atm))
        # and water not included in Pybel_objects
        if (i == H2Oi):
            Psat[0, i] = Psat_water
            if (TEMP == 298.15):
                Psat_Pa_rec[i] = Psat[0, i]
            else:
                [_, Psat_Pa_rec[i], _] = water_calc(298.15, 0.5, si.N_A)
            OC[0, i] = 0.
            continue

        if (spec_namelist[i] == 'O3'):
            # vapour pressure of ozone from https://doi.org/10.1063/1.1700683
            Psat[0, i] = np.log10(
                (8.25313 -
                 (814.941587 / TEMP) - 0.001966943 * TEMP) * 1.31579e-3)
            if (TEMP == 298.15):
                Psat_Pa_rec[i] = Psat[0, i]
            else:
                Psat_Pa_rec[i] = np.log10(
                    (8.25313 - (814.941587 / 298.15) - 0.001966943 * 298.15) *
                    1.31579e-3)
            OC[0, i] = 0.
            continue

        # use EVAPORATION method for vapour pressure (log10(atm)) of HOMs
        if 'api_' in spec_namelist[i] or 'API_' in spec_namelist[i]:
            Psat[0, i] = ((vapour_pressures.myrdal_and_yalkowsky(
                Pybel_objects[i], TEMP,
                boiling_points.nannoolal(Pybel_objects[i]))))

            if (TEMP == 298.15):
                Psat_Pa_rec[i] = Psat_Pa[0, i]
            else:
                Psat_Pa_rec[i] = ((vapour_pressures.myrdal_and_yalkowsky(
                    Pybel_objects[i], 298.15,
                    boiling_points.nannoolal(Pybel_objects[i]))))
        else:
            # vapour pressure (log10(atm)) (eq. 6 of Nannoolal et al. (2008), with dB of
            # that equation given by eq. 7 of same reference)
            Psat[0, i] = ((vapour_pressures.nannoolal(
                Pybel_objects[i], TEMP,
                boiling_points.nannoolal(Pybel_objects[i]))))

            if (TEMP == 298.15):
                Psat_Pa_rec[i] = Psat_Pa[0, i]
            else:
                Psat_Pa_rec[i] = ((vapour_pressures.nannoolal(
                    Pybel_objects[i], 298.15,
                    boiling_points.nannoolal(Pybel_objects[i]))))

        # O:C ratio determined from SMILES string
        if (rel_SMILES[i].count('C') > 0):
            OC[0, i] = (rel_SMILES[i].count('O')) / (rel_SMILES[i].count('C'))
        else:
            OC[0, i] = 0.

    ish = (Psat == 0.)

    Psat = (10.**Psat) * 101325.  # convert to Pa from atm
    Psat_Pa_rec = (10.**Psat_Pa_rec) * 101325  # convert to Pa from atm
    # retain low volatility where wanted following unit conversion
    Psat[ish] = 0.

    # list to remember which components have vapour pressures specified
    vi_rec = []

    # manually assigned vapour pressures (Pa)
    if (len(vol_Comp) > 0 and ode_gen_flag == 0):
        for i in range(len(vol_Comp)):
            # index of component in list of components
            vol_indx = spec_namelist.index(vol_Comp[i])
            Psat[0, vol_indx] = volP[i]
            Psat_Pa_rec[vol_indx] = volP[i]
            vi_rec.append(vol_indx)

    # ensure if nucleating component is core that it is involatile
    if (nuc_comp == 'core'):
        Psat[0, nuci] = 0.

    Psat_Pa = np.zeros(
        (1, num_comp))  # for storing vapour pressures in Pa (Pa)
    Psat_Pa[0, :] = Psat[0, :]

    # convert saturation vapour pressures from Pa to # molecules/cm3 (air) using ideal
    # gas law, R has units cc.Pa/K.mol
    Psat = Psat * (NA / ((si.R * 1.e6) * TEMP))
    # now, in preparation for ode solver, repeat over number of size bins
    if (num_asb > 0):
        Psat = np.repeat(Psat, num_asb, axis=0)

    return (Psat, y_dens, Psat_Pa, Psat_Pa_rec, OC)
Ejemplo n.º 5
0
def volat_calc(spec_list, Pybel_objects, TEMP, H2Oi, num_speci, Psat_water, vol_Comp, 
				volP, testf, corei, seed_name, pconc, umansysprop_update, core_dens, spec_namelist,
				ode_gen_flag, nuci, nuc_comp):

	# inputs: ------------------------------------------------------------
	# spec_list - array of SMILE strings for components 
	# (omitting water and core, if present)
	# Pybel_objects - list of Pybel objects representing the species in spec_list
	# (omitting water and core, if present)
	# TEMP - temperature (K) in chamber at time function called
	# vol_Comp - names of components (corresponding to those in chemical scheme file)
	# 			that have vapour pressures manually set in volP
	# testf - flag for whether in normal mode (0) or testing mode (1)
	# corei - index of seed particle component
	# seed_name - name(s) of components(s) comprising seed particles
	# pconc - initial number concentration of particles (#/cc (air))
	# umansysprop_update - marker for cloning UManSysProp so that latest version used
	# core_dens - density of core material (g/cc (liquid/solid density))
	# spec_namelist - list of components' names in chemical equation file
	# ode_gen_flag - whether or not called from front or ode_gen
	# nuci - index of nucleating component
	# nuc_comp - name of nucleating component
	# ------------------------------------------------------------
	
	
	
	if testf==1:
		return(0,0,0) # return dummies
		
	cwd = os.getcwd() # address of current working directory
	if umansysprop_update == 1:
		print('Cloning latest version of UManSysProp in volat_calc module')
		# download latest version of umansysprop
		
		# check if there is an existing umansysprop folder
		if os.path.isdir(cwd + '/umansysprop'): 
			def handleRemoveReadonly(func, path, exc):
				excvalue = exc[1]
				if not os.access(path, os.W_OK):
					# Is the error an access error ?
					os.chmod(path, stat.S_IWUSR)
					func(path)
				else:
					raise
			# remove existing folder, onerror will change permission of directory if 
			# needed
			shutil.rmtree(cwd + '/umansysprop', ignore_errors=False, onerror=handleRemoveReadonly)
		
		git_url = 'https://github.com/loftytopping/UManSysProp_public.git'
		Repo.clone_from(git_url, (cwd + '/umansysprop'))
	
	# point to umansysprop folder
	sys.path.insert(1, (cwd + '/umansysprop')) # address for updated version
	
	from umansysprop import boiling_points
	from umansysprop import vapour_pressures
	from umansysprop import liquid_densities

	NA = si.Avogadro # Avogadro's number (molecules/mol)
	y_dens = np.zeros((num_speci, 1)) # components' liquid density (kg/m3)
	Psat = np.zeros((num_speci, 1)) # species' vapour pressure

	
	if ode_gen_flag == 0: # estimate densities
		
		for i in range (num_speci):
			
			# density estimation ---------------------------------------------------------
			if i == H2Oi:
				y_dens[i] = 1.0*1.0E3 # (kg/m3 (particle))
				continue
			# core properties
			if (i == corei[0]):
				y_dens[i] = core_dens*1.e3 # core density (kg/m3 (particle))
				continue
			# nucleating component density, if component is core (kg/m3 (particle))
			if i == nuci and nuc_comp[0] == 'core': 
				y_dens[i] = 1.0*1.0E3
				continue
			if spec_list[i] == '[HH]': # omit H2 as unliked by liquid density code
				# liquid density code does not like H2, so manually input kg/m3
				y_dens[i] = 1.0e3
			else:
				# density (convert from g/cc to kg/m3)
				y_dens[i] = liquid_densities.girolami(Pybel_objects[i])*1.0E3
			# ----------------------------------------------------------------------------
	
	# estimate vapour pressures (log10(atm))
	for i in range (num_speci):
		
		if (i == corei[0]): # if this core component
			continue # core component not included in Pybel_objects
		if i == nuci and nuc_comp[0] == 'core':
			continue # core component not included in Pybel_objects
		
		# water vapour pressure already given by Psat_water (log10(atm))
		if i == H2Oi:
			Psat[i] = Psat_water
			continue # water not included in Pybel_objects
		
		# vapour pressure (log10 atm) (# eq. 6 of Nannoolal et al. (2008), with dB of 
		# that equation given by eq. 7 of same reference)
		Psat[i] = ((vapour_pressures.nannoolal(Pybel_objects[i], TEMP, 
						boiling_points.nannoolal(Pybel_objects[i]))))
	
	ish = Psat==0.0
	
	Psat = (np.power(10.0, Psat)*101325.0) # convert to Pa from atm
	# retain low volatility where wanted
	Psat[ish] = 0.0
	
	# manually assigned vapour pressures (Pa)
	if len(vol_Comp)>0 and ode_gen_flag==0:
		for i in range (len(vol_Comp)):
			# index of component in list of components
			vol_indx = spec_namelist.index(vol_Comp[i])
			Psat[vol_indx, 0] = volP[i]
	# ensure if nucleating component is core that it is involatile
	if nuc_comp == 'core':
		Psat[nuci, 0] = 0.0
	
	Psat_Pa = np.zeros((len(Psat), 1)) # for storing vapour pressures in Pa (Pa)
	Psat_Pa[:, 0] = Psat[:, 0]
    
	# convert saturation vapour pressures from Pa to molecules/cc (air) using ideal
	# gas law, R has units cc.Pa/K.mol
	Psat = Psat*(NA/((si.R*1.e6)*TEMP))
	
	return Psat, y_dens, Psat_Pa
Ejemplo n.º 6
0
def volat_calc(spec_list, Pybel_objects, TEMP, H2Oi, num_speci, Psat_water,
               voli, volP, testf, corei):

    # ------------------------------------------------------------
    # inputs:
    # spec_list - array of SMILE strings for components
    # (omitting water and core, if present)
    # Pybel_objects - list of Pybel objects representing the species in spec_list
    # (omitting water and core, if present)
    # testf - flag for whether in normal mode (0) or testing mode (1)
    # corei - index of seed particle component
    # ------------------------------------------------------------

    if testf == 1:
        return (0, 0, 0)  # return dummies

    # if voli is in relative index (-n), change to absolute
    for i in range(len(voli)):
        if voli[i] < 0:
            voli[i] = num_speci + voli[i]

    NA = si.Avogadro  # Avogadro's number (molecules/mol)
    y_dens = np.zeros((num_speci, 1))  # components' liquid density (kg/m3)
    Psat = np.zeros((num_speci, 1))  # species' vapour pressure

    for i in range(num_speci):

        # omit estimation for water as it's value is already given in Psat_water
        # (log10(atm))
        if i == H2Oi:
            Psat[i] = Psat_water
            y_dens[i] = 1.0 * 1.0E3  # (kg/m3 (particle))
            continue
        if i == corei:  # core properties (only used if seed particles present)
            y_dens[i] = 1.77 * 1.0E3  # core density (kg/m3 (particle))
            continue
        if spec_list[i] == '[HH]':  # omit H2 as unliked by liquid density code
            # liquid density code does not like H2, so manually input kg/m3
            y_dens[i] = 1.0e3
        else:
            # density (convert from g/cc to kg/m3)
            y_dens[i] = liquid_densities.girolami(Pybel_objects[i]) * 1.0E3

        # vapour pressure (log10 atm) (# eqn. 6 of Nannoolal et al. (2008), with dB of
        # that equation given by eq. 7)
        Psat[i] = ((vapour_pressures.nannoolal(
            Pybel_objects[i], TEMP,
            boiling_points.nannoolal(Pybel_objects[i]))))

    Psat = (np.power(10.0, Psat) * 101325.0)  # convert to Pa
    # manually assigned vapour pressures (Pa) (including seed component (if applicable))
    if len(voli) > 0:
        Psat[voli, 0] = volP
    Psat_Pa = np.zeros(
        (len(Psat), 1))  # for storing vapour pressures in Pa (Pa)
    Psat_Pa[:, 0] = Psat[:, 0]

    # convert saturation vapour pressures from Pa to molecules/cc (air) using ideal
    # gas law, R has units cc.Pa/K.mol
    Psat = Psat * (NA / (8.3144598e6 * TEMP))

    return Psat, y_dens, Psat_Pa