Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
56c6014
add computeConsequencesWithReconstruction
sebastianrowan Jan 26, 2026
465b359
add brainstorm comments
sebastianrowan Jan 26, 2026
8c156c1
add reconstruction curves to occtypes.json
sebastianrowan Jan 26, 2026
7ad8877
include reconstruction in computeConsequences(). possibly temporary.
sebastianrowan Jan 26, 2026
abacef3
add reconstruction to computeConsequences
sebastianrowan Jan 26, 2026
c7ddc10
add default reconstruction function
sebastianrowan Jan 26, 2026
e92de2d
Add the "default" damage function for all reconstruction.
sebastianrowan Jan 26, 2026
7a3249f
roll back implementation pending unit tests
sebastianrowan Jan 28, 2026
6c5764c
add computeConsequencesMulti().
sebastianrowan Jan 29, 2026
1bb954d
undo unneccesary edits
sebastianrowan Jan 29, 2026
ec0a212
update reconstruction following g2crm methods. close but not quite ri…
sebastianrowan Feb 1, 2026
8786c04
calculate pct_complete based on total damage, not just previous event
sebastianrowan Feb 2, 2026
fef6b6c
fix test completion date chack
sebastianrowan Feb 2, 2026
f3497b2
add MultiHazardEvent interface and corresponding Compute logic for st…
sebastianrowan Feb 4, 2026
0646fb2
add json hazardprovider for ArrivalDepthandDurationEventMulti
sebastianrowan Feb 5, 2026
8c8f1ff
change format of results for computeConsequencesMultiHazard
sebastianrowan Feb 5, 2026
f464e67
test StreamAbstract with MultiHazardProvider
sebastianrowan Feb 5, 2026
c1a5dcd
assert expected values on test
sebastianrowan Feb 6, 2026
c8565d9
add csv hazardprovider
sebastianrowan Feb 6, 2026
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
24 changes: 24 additions & 0 deletions compute/simulation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,30 @@ func Test_StreamAbstract_MultiFrequency(t *testing.T) {
//compute consequences.
StreamAbstractMultiFrequency(hazardProviders, frequencies, nsp, w)
}

func Test_StreamAbstract_MultiHazard(t *testing.T) {
//initialize the NSI API structure provider
nsp := structureprovider.InitNSISPwithOcctypeFilePath("/workspaces/go-consequences/data/lifecycle/occtypes_reconstruction.json")
now := time.Now()
fmt.Println(now)

root := "/workspaces/go-consequences/data/lifecycle/"
filepath := root + "test_arrival-depth-duration_hazards.json"
w, _ := resultswriters.InitSpatialResultsWriter(root+"multihazardtest_consequences.json", "results", "GeoJSON")
//w := consequences.InitSummaryResultsWriterFromFile(root + "_consequences_SUMMARY.json")
//create a result writer based on the name of the depth grid.
//w, _ := resultswriters.InitGpkResultsWriter(root+"_consequences_nsi.gpkg", "nsi_result")
defer w.Close()
//initialize a hazard provider based on the depth grid.
dfr, err := hazardproviders.InitADDMHP(filepath)
if err != nil {
panic(err)
}
//compute consequences.
StreamAbstract(dfr, nsp, w)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ismt it cool that we get such robustly different behavior out of streamabstract when we leverage the abstractions!

fmt.Println(time.Since(now))
}

func Test_Config(t *testing.T) {
config := Config{
StructureProviderInfo: structureprovider.StructureProviderInfo{
Expand Down
111 changes: 111 additions & 0 deletions hazardproviders/csv_multi_hazard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package hazardproviders

import (
"encoding/csv"
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/USACE/go-consequences/geography"
"github.com/USACE/go-consequences/hazards"
)

type csvArrivalDepthDurationMultiHazardProvider struct {
f *os.File
arrivals []time.Time
depths []float64
durations []float64
process HazardFunction
bbox geography.BBox
}

func InitCSV(fp string, b geography.BBox) (csvArrivalDepthDurationMultiHazardProvider, error) {
fmt.Println("Connecting to: " + fp)
file, err := os.Open(fp)
if err != nil {
panic(err)
}

reader := csv.NewReader(file)

rows, err := reader.ReadAll()
if err != nil {
panic(err)
}
arrivals := make([]time.Time, len(rows)-1)
depths := make([]float64, len(rows)-1)
durations := make([]float64, len(rows)-1)

for i, row := range rows[1:] {
year, err := strconv.Atoi(row[2])
if err != nil {
panic(err)
}
month, err := strconv.Atoi(strings.TrimSpace(row[3]))
if err != nil {
panic(err)
}
day, err := strconv.Atoi(strings.TrimSpace(row[4]))
if err != nil {
panic(err)
}
at := time.Date(year, time.Month(month), int(day), 0, 0, 0, 0, time.UTC)
depth, err := strconv.ParseFloat(row[5], 64)
if err != nil {
panic(err)
}
dur, err := strconv.ParseFloat(row[6], 64)
if err != nil {
panic(err)
}

arrivals[i] = at
depths[i] = depth
durations[i] = dur
}

return csvArrivalDepthDurationMultiHazardProvider{
f: file,
arrivals: arrivals,
depths: depths,
durations: durations,
process: ArrivalDepthAndDurationHazardFunction(),
bbox: b,
}, nil

}

func (c csvArrivalDepthDurationMultiHazardProvider) Close() {
c.f.Close()
}

func (c csvArrivalDepthDurationMultiHazardProvider) Hazard(l geography.Location) (hazards.HazardEvent, error) {
var hm hazards.ArrivalDepthandDurationEventMulti
if c.bbox.Contains(l) {
for i, d := range c.depths {
hd := hazards.HazardData{
Depth: d,
Velocity: 0,
ArrivalTime: c.arrivals[i],
Erosion: 0,
Duration: c.durations[i],
WaveHeight: 0,
Salinity: false,
Qualitative: "",
}
var h hazards.HazardEvent
h, err := c.process(hd, h)
if err != nil {
panic(err)
}
hm.Append(h.(hazards.ArrivalDepthandDurationEvent))
}
}
return &hm, nil
}

func (c csvArrivalDepthDurationMultiHazardProvider) HazardBoundary() (geography.BBox, error) {
return c.bbox, nil
}
66 changes: 66 additions & 0 deletions hazardproviders/csv_multi_hazard_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package hazardproviders

import (
"testing"
"time"

"github.com/USACE/go-consequences/geography"
"github.com/USACE/go-consequences/hazards"
)

func TestInitCSV(t *testing.T) {
file := "/workspaces/go-consequences/data/lifecycle/test_arrival-depth-duration_hazards.csv"

expectedDepths := []float64{1.0, 1.0, 1.0, 2.0, 2.0}
expectedDurations := []float64{0.0, 5.0, 0.0, 0.0, 0.0}
et1 := time.Date(1984, time.Month(1), 1, 0, 0, 0, 0, time.UTC)
et2 := time.Date(1984, time.Month(1), 11, 0, 0, 0, 0, time.UTC)
et3 := time.Date(1984, time.Month(1), 21, 0, 0, 0, 0, time.UTC)
et4 := time.Date(1985, time.Month(1), 1, 0, 0, 0, 0, time.UTC)
et5 := time.Date(1985, time.Month(1), 11, 0, 0, 0, 0, time.UTC)
expectedArrivals := []time.Time{et1, et2, et3, et4, et5}

b := geography.BBox{
// xMin, yMin, xMax, yMax
Bbox: []float64{-71.1, 43, -71, 43.1},
}
ADDMHP, err := InitCSV(file, b)
if err != nil {
panic(err)
}
defer ADDMHP.Close()

loc := geography.Location{
X: -71.05,
Y: 43.05,
SRID: "",
}

haz, err := ADDMHP.Hazard(loc)
h := haz.(*hazards.ArrivalDepthandDurationEventMulti)
if err != nil {
panic(err)
}

for {
edepth := expectedDepths[h.Index()]
edur := expectedDurations[h.Index()]
earr := expectedArrivals[h.Index()]

if h.Depth() != edepth {
t.Errorf("Event at index %d had Depth = %v. Expected: %3.2f", h.Index(), h.Depth(), edepth)
}
if h.Duration() != edur {
t.Errorf("Event at index %d had Duration = %v. Expected: %3.2f", h.Index(), h.Duration(), edur)
}
if h.Depth() != edepth {
t.Errorf("Event at index %d had ArrivalTime = %v. Expected: %v", h.Index(), h.ArrivalTime(), earr)
}

if h.HasNext() {
h.Increment()
} else {
break
}
}
}
19 changes: 19 additions & 0 deletions hazardproviders/interfaces.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh. did you write this code? it is a pretty neat idea.

Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ func ArrivalAndDurationHazardFunction() HazardFunction {
}
}

func ArrivalDepthAndDurationHazardFunction() HazardFunction {
return func(valueIn hazards.HazardData, hazard hazards.HazardEvent) (hazards.HazardEvent, error) {
d := hazards.ArrivalDepthandDurationEvent{}
d.SetDepth(valueIn.Depth)
d.SetDuration(valueIn.Duration)
d.SetArrivalTime(valueIn.ArrivalTime)
return d, nil
}
}

func DepthAndDurationHazardFunction() HazardFunction {
return func(valueIn hazards.HazardData, hazard hazards.HazardEvent) (hazards.HazardEvent, error) {
d := hazards.ArrivalDepthandDurationEvent{}
d.SetDepth(valueIn.Depth)
d.SetDuration(valueIn.Duration)
return d, nil
}
}

// NoHazardFoundError is an error for a situation where no hazard could be computed for the given args
type NoHazardFoundError struct {
Input string
Expand Down
109 changes: 109 additions & 0 deletions hazardproviders/json_multi_hazard.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package hazardproviders

import (
"encoding/json"
"fmt"
"os"
"time"

"github.com/USACE/go-consequences/geography"
"github.com/USACE/go-consequences/hazards"
)

type jsonArrivalDepthDurationMultiHazardProvider struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using cogs at this point is overkill

arrivals []time.Time
depthCRs []cogReader
durations []float64
process HazardFunction
}

type ADDProperties struct {
Year float64 `json:"year"`
Month float64 `json:"month"`
Day float64 `json:"day"`
Depth float64 `json:"depth"`
Depthgrid string `json:"depthgrid"`
Duration float64 `json:"duration"`
Xmin float64 `json:"xmin"`
Xmax float64 `json:"xmax"`
Ymin float64 `json:"ymin"`
Ymax float64 `json:"ymax"`
}

type ADDEvents struct {
Events []ADDProperties `json:"events"`
}

func InitADDMHP(fp string) (jsonArrivalDepthDurationMultiHazardProvider, error) {
fmt.Println("Connecting to: " + fp)
c, err := os.ReadFile(fp)
if err != nil {
panic(err)
}

var events ADDEvents
json.Unmarshal(c, &events)

arrivalTimes := make([]time.Time, len(events.Events))
durations := make([]float64, len(events.Events))
depthCRs := make([]cogReader, len(events.Events))

for i, e := range events.Events {
at := time.Date(int(e.Year), time.Month(e.Month), int(e.Day), 0, 0, 0, 0, time.UTC)
cr, err := initCR(e.Depthgrid)
if err != nil {
panic(err)
}

arrivalTimes[i] = at
durations[i] = e.Duration
depthCRs[i] = cr
}

return jsonArrivalDepthDurationMultiHazardProvider{
arrivals: arrivalTimes,
depthCRs: depthCRs,
durations: durations,
process: ArrivalDepthAndDurationHazardFunction(),
}, nil
}

func (j jsonArrivalDepthDurationMultiHazardProvider) Close() {
for _, c := range j.depthCRs {
c.Close()
}
}

func (j jsonArrivalDepthDurationMultiHazardProvider) Hazard(l geography.Location) (hazards.HazardEvent, error) {
var hm hazards.ArrivalDepthandDurationEventMulti
for i, cr := range j.depthCRs {
d, err := cr.ProvideValue(l)
if err != nil {
return hm, err
}
hd := hazards.HazardData{
Depth: d,
Velocity: 0,
ArrivalTime: j.arrivals[i],
Erosion: 0,
Duration: j.durations[i],
WaveHeight: 0,
Salinity: false,
Qualitative: "",
}
var h hazards.HazardEvent
h, err = j.process(hd, h)
if err != nil {
panic(err)
}
hm.Append(h.(hazards.ArrivalDepthandDurationEvent))
}
return &hm, nil
}

func (j jsonArrivalDepthDurationMultiHazardProvider) HazardBoundary() (geography.BBox, error) {
// We'll probably want to do something different here.
// Probably allow user to define study bbox with a
// shapefile/geojson/directly entering bbox coords
return j.depthCRs[0].GetBoundingBox()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is great for now

}
Loading