Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
everything compiling
  • Loading branch information
prehner committed Jun 16, 2025
commit bbb744cc74697b03739eee7010d812bdbec562ab
1 change: 0 additions & 1 deletion crates/feos-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ rayon = { workspace = true, optional = true }
typenum = { workspace = true }
itertools = { workspace = true }
arrayvec = { workspace = true, features = ["serde"] }
petgraph = { workspace = true }

[dev-dependencies]
approx = { workspace = true }
Expand Down
4 changes: 2 additions & 2 deletions crates/feos-core/src/cubic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl PengRobinson {
/// Create a new equation of state from a set of parameters.
pub fn new(parameters: PengRobinsonParameters) -> Self {
let [tc, pc, ac] = parameters.collate(|r| [r.tc, r.pc, r.acentric_factor]);
let [k_ij] = parameters.collate_binary(|br| [br.unwrap_or_default()]);
let [k_ij] = parameters.collate_binary(|&br| [br]);

let a = 0.45724 * tc.powi(2) * KB_A3 / &pc;
let b = 0.07780 * &tc * KB_A3 / pc;
Expand All @@ -107,7 +107,7 @@ impl PengRobinson {

impl Components for PengRobinson {
fn components(&self) -> usize {
self.parameters.pure_records.len()
self.tc.len()
}

fn subset(&self, component_list: &[usize]) -> Self {
Expand Down
275 changes: 275 additions & 0 deletions crates/feos-core/src/parameter/association.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use super::{Binary, BinaryRecord, GroupCount, Pure};
use crate::{FeosError, FeosResult, parameter::PureRecord};
use arrayvec::ArrayString;
use ndarray::Array1;
use num_traits::Zero;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};

type SiteId = ArrayString<8>;

Expand Down Expand Up @@ -71,3 +75,274 @@ impl<A> BinaryAssociationRecord<A> {
}
}
}

#[derive(Clone, Copy, Debug)]
pub struct AssociationSite<A> {
pub assoc_comp: usize,
pub id: SiteId,
pub n: f64,
pub parameters: A,
}

impl<A> AssociationSite<A> {
fn new(assoc_comp: usize, id: SiteId, n: f64, parameters: A) -> Self {
Self {
assoc_comp,
id,
n,
parameters,
}
}
}

/// Parameter set required for the SAFT association Helmoltz energy
/// contribution and functional.
#[derive(Clone)]
pub struct AssociationParameters<A> {
pub component_index: Array1<usize>,
pub sites_a: Array1<AssociationSite<Option<A>>>,
pub sites_b: Array1<AssociationSite<Option<A>>>,
pub sites_c: Array1<AssociationSite<Option<A>>>,
pub binary_ab: Vec<Binary<A, ()>>,
pub binary_cc: Vec<Binary<A, ()>>,
}

impl<A: Clone> AssociationParameters<A> {
pub fn new<P, B>(
pure_records: &[PureRecord<P, A>],
binary_records: &[BinaryRecord<usize, B, A>],
) -> FeosResult<Self> {
let mut sites_a = Vec::new();
let mut sites_b = Vec::new();
let mut sites_c = Vec::new();

for (i, record) in pure_records.iter().enumerate() {
for site in record.association_sites.iter() {
let par = &site.parameters;
if site.na > 0.0 {
sites_a.push(AssociationSite::new(i, site.id, site.na, par.clone()));
}
if site.nb > 0.0 {
sites_b.push(AssociationSite::new(i, site.id, site.nb, par.clone()));
}
if site.nc > 0.0 {
sites_c.push(AssociationSite::new(i, site.id, site.nc, par.clone()));
}
}
}

let indices_a: HashMap<_, _> = sites_a
.iter()
.enumerate()
.map(|(i, site)| ((site.assoc_comp, &site.id), i))
.collect();

let indices_b: HashMap<_, _> = sites_b
.iter()
.enumerate()
.map(|(i, site)| ((site.assoc_comp, &site.id), i))
.collect();

let indices_c: HashMap<_, _> = sites_c
.iter()
.enumerate()
.map(|(i, site)| ((site.assoc_comp, &site.id), i))
.collect();

let index_set: HashSet<_> = indices_a
.keys()
.chain(indices_b.keys())
.chain(indices_c.keys())
.copied()
.collect();

let mut binary_ab = Vec::new();
let mut binary_cc = Vec::new();
for br in binary_records {
let i = br.id1;
let j = br.id2;
for record in &br.association_sites {
let a = &record.id1;
let b = &record.id2;
if !index_set.contains(&(i, a)) {
return Err(FeosError::IncompatibleParameters(format!(
"No association site {a} on component {i}"
)));
}
if !index_set.contains(&(j, b)) {
return Err(FeosError::IncompatibleParameters(format!(
"No association site {b} on component {j}"
)));
}
if let (Some(x), Some(y)) = (indices_a.get(&(i, a)), indices_b.get(&(j, b))) {
binary_ab.push(Binary::new(*x, *y, record.parameters.clone(), ()));
}
if let (Some(y), Some(x)) = (indices_b.get(&(i, a)), indices_a.get(&(j, b))) {
binary_ab.push(Binary::new(*x, *y, record.parameters.clone(), ()));
}
if let (Some(x), Some(y)) = (indices_c.get(&(i, a)), indices_c.get(&(j, b))) {
binary_cc.push(Binary::new(*x, *y, record.parameters.clone(), ()));
binary_cc.push(Binary::new(*y, *x, record.parameters.clone(), ()));
}
}
}
let component_index = (0..pure_records.len()).collect();

Ok(Self {
component_index,
sites_a: Array1::from_vec(sites_a),
sites_b: Array1::from_vec(sites_b),
sites_c: Array1::from_vec(sites_c),
binary_ab,
binary_cc,
})
}

pub fn new_hetero<P, C: GroupCount>(
pure_records: &[Pure<P, C>],
association_sites: &[Vec<AssociationRecord<A>>],
binary_records: &[Binary<Vec<BinaryAssociationRecord<A>>, ()>],
) -> FeosResult<Self> {
let mut sites_a = Vec::new();
let mut sites_b = Vec::new();
let mut sites_c = Vec::new();

for (i, (record, sites)) in pure_records.iter().zip(association_sites).enumerate() {
for site in sites.iter() {
let par = &site.parameters;
if site.na > 0.0 {
let na = site.na * record.count.into_f64();
sites_a.push(AssociationSite::new(i, site.id, na, par.clone()));
}
if site.nb > 0.0 {
let nb = site.nb * record.count.into_f64();
sites_b.push(AssociationSite::new(i, site.id, nb, par.clone()));
}
if site.nc > 0.0 {
let nc = site.nc * record.count.into_f64();
sites_c.push(AssociationSite::new(i, site.id, nc, par.clone()));
}
}
}

let indices_a: HashMap<_, _> = sites_a
.iter()
.enumerate()
.map(|(i, site)| ((site.assoc_comp, &site.id), i))
.collect();

let indices_b: HashMap<_, _> = sites_b
.iter()
.enumerate()
.map(|(i, site)| ((site.assoc_comp, &site.id), i))
.collect();

let indices_c: HashMap<_, _> = sites_c
.iter()
.enumerate()
.map(|(i, site)| ((site.assoc_comp, &site.id), i))
.collect();

let index_set: HashSet<_> = indices_a
.keys()
.chain(indices_b.keys())
.chain(indices_c.keys())
.copied()
.collect();

let mut binary_ab = Vec::new();
let mut binary_cc = Vec::new();
for br in binary_records {
let i = br.id1;
let j = br.id2;
for record in &br.model_record {
let a = &record.id1;
let b = &record.id2;
if !index_set.contains(&(i, a)) {
return Err(FeosError::IncompatibleParameters(format!(
"No association site {a} on component {i}"
)));
}
if !index_set.contains(&(j, b)) {
return Err(FeosError::IncompatibleParameters(format!(
"No association site {b} on component {j}"
)));
}
if let (Some(x), Some(y)) = (indices_a.get(&(i, a)), indices_b.get(&(j, b))) {
binary_ab.push(Binary::new(*x, *y, record.parameters.clone(), ()));
}
if let (Some(y), Some(x)) = (indices_b.get(&(i, a)), indices_a.get(&(j, b))) {
binary_ab.push(Binary::new(*x, *y, record.parameters.clone(), ()));
}
if let (Some(x), Some(y)) = (indices_c.get(&(i, a)), indices_c.get(&(j, b))) {
binary_cc.push(Binary::new(*x, *y, record.parameters.clone(), ()));
binary_cc.push(Binary::new(*y, *x, record.parameters.clone(), ()));
}
}
}

let component_index = pure_records.iter().map(|pr| pr.component_index).collect();

Ok(Self {
component_index,
sites_a: Array1::from_vec(sites_a),
sites_b: Array1::from_vec(sites_b),
sites_c: Array1::from_vec(sites_c),
binary_ab,
binary_cc,
})
}

pub fn is_empty(&self) -> bool {
(self.sites_a.is_empty() | self.sites_b.is_empty()) & self.sites_c.is_empty()
}

pub fn subset(&self, component_list: &[usize]) -> Self {
let keep_group = self.component_index.map(|i| component_list.contains(i));

let filter = |x: &Array1<AssociationSite<Option<A>>>| {
x.iter()
.filter(|&s| keep_group[s.assoc_comp])
.cloned()
.collect()
};
let sites_a = filter(&self.sites_a);
let sites_b = filter(&self.sites_b);
let sites_c = filter(&self.sites_c);

let binary_ab = self
.binary_ab
.iter()
.filter(|b| {
keep_group[self.sites_a[b.id1].assoc_comp]
&& keep_group[self.sites_a[b.id2].assoc_comp]
})
.cloned()
.collect();
let binary_cc = self
.binary_cc
.iter()
.filter(|b| {
keep_group[self.sites_c[b.id1].assoc_comp]
&& keep_group[self.sites_c[b.id2].assoc_comp]
})
.cloned()
.collect();
let component_index = self
.component_index
.iter()
.zip(keep_group)
.filter(|&(_, k)| k)
.map(|(c, _)| c)
.copied()
.collect();
Self {
component_index,
sites_a,
sites_b,
sites_c,
binary_ab,
binary_cc,
}
}
}
Loading
Loading