///|
pub enum StreamDirection {
Inlet
Outlet
} derive(Debug, Eq)
///|
pub fn stream_directions() -> Array[StreamDirection] {
[Inlet, Outlet]
}
///|
pub fn inlet() -> StreamDirection {
Inlet
}
///|
pub fn outlet() -> StreamDirection {
Outlet
}
///|
pub struct ComponentFlow {
component : String
value : Float
unit : MeasureUnit
} derive(Debug, Eq)
///|
pub fn component_flow(
component : String,
value : Float,
unit : MeasureUnit,
) -> ComponentFlow {
{ component, value, unit }
}
///|
pub struct ProcessStream {
name : String
direction : StreamDirection
components : Array[ComponentFlow]
} derive(Debug, Eq)
///|
pub fn stream(
name : String,
direction : StreamDirection,
components : Array[ComponentFlow],
) -> ProcessStream {
{ name, direction, components }
}
///|
pub struct ComponentBalance {
component : String
inlet : Float
outlet : Float
net : Float
relative_error : Float
} derive(Debug, Eq)
///|
pub struct MaterialBalance {
components : Array[ComponentBalance]
total_inlet : Float
total_outlet : Float
} derive(Debug, Eq)
///|
fn find_component(names : Array[String], name : String) -> Int? {
for index, item in names {
if item == name {
return Some(index)
}
}
None
}
///|
pub fn material_balance(streams : Array[ProcessStream]) -> MaterialBalance {
let names : Array[String] = []
let inlet : Array[Float] = []
let outlet : Array[Float] = []
let mut total_inlet : Float = 0.0
let mut total_outlet : Float = 0.0
for item in streams {
for flow in item.components {
if item.direction is Inlet {
match find_component(names, flow.component) {
Some(index) => inlet[index] = inlet[index] + flow.value
None => {
names.push(flow.component)
inlet.push(flow.value)
outlet.push(0.0)
}
}
total_inlet = total_inlet + flow.value
} else {
match find_component(names, flow.component) {
Some(index) => outlet[index] = outlet[index] + flow.value
None => {
names.push(flow.component)
inlet.push(0.0)
outlet.push(flow.value)
}
}
total_outlet = total_outlet + flow.value
}
}
}
let components : Array[ComponentBalance] = []
for index, name in names {
let input = inlet[index]
let output = outlet[index]
let denominator = if input.abs() > output.abs() {
input.abs()
} else {
output.abs()
}
let relative_error : Float = if denominator == 0.0 {
0.0
} else {
(input - output).abs() / denominator
}
components.push({
component: name,
inlet: input,
outlet: output,
net: input - output,
relative_error,
})
}
{ components, total_inlet, total_outlet }
}
///|
pub fn MaterialBalance::is_closed(
self : MaterialBalance,
tolerance? : Float = 0.001,
) -> Bool {
for item in self.components {
if item.relative_error > tolerance {
return false
}
}
true
}
///|
pub fn MaterialBalance::limiting_component(self : MaterialBalance) -> String? {
if self.components.length() == 0 {
return None
}
let mut best = self.components[0]
for item in self.components[1:] {
if item.relative_error > best.relative_error {
best = item
}
}
Some(best.component)
}
///|
pub fn MaterialBalance::component(
self : MaterialBalance,
name : String,
) -> ComponentBalance? {
for item in self.components {
if item.component == name {
return Some(item)
}
}
None
}
///|
pub fn MaterialBalance::closure_percent(self : MaterialBalance) -> Float {
if self.total_inlet == 0.0 {
if self.total_outlet == 0.0 {
100.0
} else {
0.0
}
} else {
100.0 * self.total_outlet / self.total_inlet
}
}
///|
pub fn MaterialBalance::to_markdown(self : MaterialBalance) -> String {
let lines : Array[String] = [
"| Component | Inlet | Outlet | Net | Relative error |", "| --- | ---: | ---: | ---: | ---: |",
]
for item in self.components {
lines.push(
"| \{item.component} | \{item.inlet} | \{item.outlet} | \{item.net} | \{item.relative_error} |",
)
}
lines.push("")
lines.push("Closure: \{self.closure_percent()}%")
lines.join("\n")
}
///|
pub struct EnergyStream {
name : String
duty : Quantity
direction : StreamDirection
} derive(Debug, Eq)
///|
pub fn energy_stream(
name : String,
duty : Quantity,
direction : StreamDirection,
) -> EnergyStream {
{ name, duty, direction }
}
///|
pub struct EnergyBalance {
inlet : Float
outlet : Float
residual : Float
streams : Array[EnergyStream]
} derive(Debug, Eq)
///|
pub fn energy_balance(streams : Array[EnergyStream]) -> EnergyBalance {
let mut inlet : Float = 0.0
let mut outlet : Float = 0.0
for item in streams {
match item.duty.convert_to(KiloJoule) {
Ok(value) =>
if item.direction is Inlet {
inlet = inlet + value
} else {
outlet = outlet + value
}
Err(_) => ()
}
}
{ inlet, outlet, residual: inlet - outlet, streams }
}
///|
pub fn EnergyBalance::is_closed(
self : EnergyBalance,
tolerance? : Float = 0.001,
) -> Bool {
self.residual.abs() <= tolerance
}
///|
pub fn EnergyBalance::specific_duty(
self : EnergyBalance,
mass : Quantity,
) -> Result[Quantity, QuantityError] {
match mass.convert_to(Kilogram) {
Ok(value) => Ok(quantity(self.residual / value, KiloJoule))
Err(error) => Err(error)
}
}