///|
pub(all) struct WeightedRainEvent {
event : RainEvent
weight : Double
} derive(Eq, Debug)
///|
pub(all) struct EventSizing {
event_name : String
depth_mm : Double
weight : Double
runoff_volume_m3 : Double
recommended_area_m2 : Double
capture_ratio : Double
drawdown_hours : Double
status : PlanStatus
} derive(Eq, Debug)
///|
pub(all) struct SeriesSummary {
event_count : Int
ready_count : Int
review_count : Int
controlling_event_name : String
max_recommended_area_m2 : Double
weighted_runoff_m3 : Double
weighted_capture_ratio : Double
results : Array[EventSizing]
} derive(Eq, Debug)
///|
pub fn weighted_event(
name : String,
depth_mm : Double,
weight : Double,
) -> WeightedRainEvent {
{ event: rain_event(name, depth_mm), weight }
}
///|
pub fn evaluate_event_series(
catchments : Array[Catchment],
events : Array[WeightedRainEvent],
spec : GardenSpec,
) -> Result[SeriesSummary, SpongeError] {
if events.length() == 0 {
return Err(InvalidWeight)
}
let results : Array[EventSizing] = []
let mut total_weight = 0.0
let mut weighted_runoff = 0.0
let mut weighted_capture = 0.0
let mut max_area = -1.0
let mut controlling_event = ""
let mut ready_count = 0
let mut review_count = 0
for event in events {
if event.weight < 0.0 {
return Err(InvalidWeight)
}
match size_rain_garden(catchments, event.event, spec) {
Err(error) => return Err(error)
Ok(plan) => {
let sizing = {
event_name: event.event.name,
depth_mm: event.event.depth_mm,
weight: event.weight,
runoff_volume_m3: plan.runoff_volume_m3,
recommended_area_m2: plan.recommended_area_m2,
capture_ratio: plan.capture_ratio,
drawdown_hours: plan.drawdown_hours,
status: plan.status,
}
results.push(sizing)
total_weight = total_weight + event.weight
weighted_runoff = weighted_runoff + plan.runoff_volume_m3 * event.weight
weighted_capture = weighted_capture + plan.capture_ratio * event.weight
if plan.recommended_area_m2 > max_area {
max_area = plan.recommended_area_m2
controlling_event = event.event.name
}
if plan.status == Ready {
ready_count = ready_count + 1
} else {
review_count = review_count + 1
}
}
}
}
if total_weight <= 0.0 {
return Err(InvalidWeight)
}
Ok({
event_count: events.length(),
ready_count,
review_count,
controlling_event_name: controlling_event,
max_recommended_area_m2: max_area,
weighted_runoff_m3: weighted_runoff / total_weight,
weighted_capture_ratio: weighted_capture / total_weight,
results,
})
}
///|
pub fn SeriesSummary::to_json(self : SeriesSummary) -> String {
let out = StringBuilder::new()
out.write_string("{")
out.write_string("\"event_count\":" + self.event_count.to_string() + ",")
out.write_string("\"ready_count\":" + self.ready_count.to_string() + ",")
out.write_string("\"review_count\":" + self.review_count.to_string() + ",")
out.write_string(
"\"controlling_event\":\"" +
escape_json(self.controlling_event_name) +
"\",",
)
out.write_string(
"\"max_recommended_area_m2\":" +
self.max_recommended_area_m2.to_string() +
",",
)
out.write_string(
"\"weighted_runoff_m3\":" + self.weighted_runoff_m3.to_string() + ",",
)
out.write_string(
"\"weighted_capture_ratio\":" +
self.weighted_capture_ratio.to_string() +
",",
)
out.write_string("\"results\":[")
for index, result in self.results {
if index > 0 {
out.write_string(",")
}
out.write_string(event_sizing_to_json(result))
}
out.write_string("]}")
out.to_string()
}
///|
pub fn SeriesSummary::to_markdown(self : SeriesSummary) -> String {
let out = StringBuilder::new()
out.write_string("# SpongeKit Event Series\n\n")
out.write_string("- Events: " + self.event_count.to_string() + "\n")
out.write_string("- Controlling event: " + self.controlling_event_name + "\n")
out.write_string(
"- Max recommended area: " +
self.max_recommended_area_m2.to_string() +
" m2\n",
)
out.write_string(
"- Weighted runoff: " + self.weighted_runoff_m3.to_string() + " m3\n",
)
out.write_string(
"- Weighted capture: " + self.weighted_capture_ratio.to_string() + "\n\n",
)
out.write_string("| Event | Rain mm | Area m2 | Capture | Status |\n")
out.write_string("| --- | ---: | ---: | ---: | --- |\n")
for result in self.results {
out.write_string(
"| " +
result.event_name +
" | " +
result.depth_mm.to_string() +
" | " +
result.recommended_area_m2.to_string() +
" | " +
result.capture_ratio.to_string() +
" | " +
result.status.label() +
" |\n",
)
}
out.to_string()
}
///|
fn event_sizing_to_json(result : EventSizing) -> String {
let out = StringBuilder::new()
out.write_string("{")
out.write_string("\"event\":\"" + escape_json(result.event_name) + "\",")
out.write_string("\"depth_mm\":" + result.depth_mm.to_string() + ",")
out.write_string("\"weight\":" + result.weight.to_string() + ",")
out.write_string(
"\"runoff_volume_m3\":" + result.runoff_volume_m3.to_string() + ",",
)
out.write_string(
"\"recommended_area_m2\":" + result.recommended_area_m2.to_string() + ",",
)
out.write_string(
"\"capture_ratio\":" + result.capture_ratio.to_string() + ",",
)
out.write_string(
"\"drawdown_hours\":" + result.drawdown_hours.to_string() + ",",
)
out.write_string("\"status\":\"" + result.status.label() + "\"}")
out.to_string()
}