///|
pub struct HeaterSizing {
  process_duty_w : Double
  design_duty_w : Double
  area_m2 : Double
  overall_u_w_per_m2_k : Double
  approach_k : Double
} derive(Debug)

///|
pub struct TankSizing {
  working_volume_m3 : Double
  volume_m3 : Double
  diameter_m : Double
  height_m : Double
  residence_time_s : Double
} derive(Debug)

///|
pub struct PumpSizing {
  hydraulic_power_w : Double
  shaft_power_w : Double
  pressure_rise_pa : Double
  flow_m3_per_s : Double
} derive(Debug)

///|
pub fn size_heater(
  process_duty_w : Double,
  approach_k : Double,
  overall_u_w_per_m2_k : Double,
  design_efficiency : Double,
) -> HeaterSizing {
  let duty = process_duty_w.abs()
  let efficiency = design_efficiency.max(0.01).min(1.0)
  let design = duty / efficiency
  let area = exchanger_area_m2(
    design,
    overall_u_w_per_m2_k.max(0.0),
    approach_k.max(0.0),
    1.0,
  )
  {
    process_duty_w: duty,
    design_duty_w: design,
    area_m2: area,
    overall_u_w_per_m2_k,
    approach_k,
  }
}

///|
pub fn size_tank(
  volumetric_flow_m3_per_s : Double,
  residence_time_s : Double,
  working_fraction : Double,
) -> TankSizing {
  let working = volumetric_flow_m3_per_s.max(0.0) * residence_time_s.max(0.0)
  let fraction = working_fraction.max(0.05).min(0.95)
  let volume = working / fraction
  let height = @math.pow(volume * 4.0 / @math.PI, 1.0 / 3.0) * 2.0
  let diameter = @math.pow(volume * 4.0 / (@math.PI * height.max(1.0e-12)), 0.5)
  {
    working_volume_m3: working,
    volume_m3: volume,
    diameter_m: diameter,
    height_m: height,
    residence_time_s,
  }
}

///|
pub fn size_pump(
  flow_m3_per_s : Double,
  pressure_rise_pa : Double,
  density_kg_per_m3 : Double,
  efficiency : Double,
) -> PumpSizing {
  let hydraulic = if density_kg_per_m3 <= 0.0 {
    0.0
  } else {
    flow_m3_per_s.max(0.0) *
    pressure_rise_pa.max(0.0) *
    density_kg_per_m3 /
    density_kg_per_m3
  }
  let shaft = if efficiency <= 0.0 {
    0.0
  } else {
    hydraulic / efficiency.min(1.0)
  }
  {
    hydraulic_power_w: hydraulic,
    shaft_power_w: shaft,
    pressure_rise_pa,
    flow_m3_per_s,
  }
}

///|
pub fn agitator_power_w(
  density_kg_per_m3 : Double,
  impeller_diameter_m : Double,
  rotational_speed_per_s : Double,
  power_number : Double,
) -> Double {
  power_number.max(0.0) *
  density_kg_per_m3.max(0.0) *
  rotational_speed_per_s.max(0.0) *
  rotational_speed_per_s.max(0.0) *
  rotational_speed_per_s.max(0.0) *
  impeller_diameter_m.max(0.0) *
  impeller_diameter_m.max(0.0) *
  impeller_diameter_m.max(0.0) *
  impeller_diameter_m.max(0.0) *
  impeller_diameter_m.max(0.0)
}

///|
pub fn vessel_wall_area_m2(diameter_m : Double, height_m : Double) -> Double {
  @math.PI * diameter_m.max(0.0) * height_m.max(0.0) +
  0.5 * @math.PI * diameter_m.max(0.0) * diameter_m.max(0.0)
}

///|
pub fn vessel_volume_m3(diameter_m : Double, height_m : Double) -> Double {
  0.25 *
  @math.PI *
  diameter_m.max(0.0) *
  diameter_m.max(0.0) *
  height_m.max(0.0)
}

///|
pub fn safety_factor_load(load_w : Double, factor : Double) -> Double {
  load_w.abs() * factor.max(1.0)
}

///|
pub fn parallel_equipment_count(
  required_capacity : Double,
  unit_capacity : Double,
) -> Int {
  if required_capacity <= 0.0 || unit_capacity <= 0.0 {
    0
  } else {
    let mut count = 0
    let mut capacity = 0.0
    for _ in 0..<10000 {
      if capacity >= required_capacity {
        break
      }
      count += 1
      capacity += unit_capacity
    }
    count
  }
}