1818
1919namespace HEC . FDA . Model . compute
2020{
21+ /// <summary>
22+ /// Holds both the channel (exterior) and floodplain (interior) frequency-stage curves.
23+ /// When no interior-exterior relationship exists, both fields reference the same curve.
24+ /// </summary>
25+ internal readonly record struct FrequencyStageCurves (
26+ PairedData ChannelStage ,
27+ PairedData FloodplainStage
28+ ) ;
29+
2130 [ StoredProperty ( "ImpactAreaScenarioSimulation" ) ]
2231 public class ImpactAreaScenarioSimulation : ValidationErrorLogger , IProgressReport
2332 {
@@ -138,8 +147,8 @@ private void SetupPerformanceThresholds(ConvergenceCriteria convergenceCriteria)
138147 //User has no levee. if the user set the default threshold themself, just use that. Otherwise calculate it...
139148 else if ( ! defaultThresholdExists ) // ONLY IF WE HAVE FIALURE DAMAGES. Validated in the VM.
140149 {
141- PairedData frequency_stage_sample = GetFrequencyStageSample ( computeIsDeterministic : true , 1 ) ;
142- ComputeConsequencesFromStageFrequency ( frequency_stage_sample , 1 , 1 , computeIsDeterministic : true , _FailureStageDamageFunctions , ConsequenceType . Damage , false , true ) ;
150+ FrequencyStageCurves curves = GetFrequencyStageSample ( computeIsDeterministic : true , 1 ) ;
151+ ComputeConsequencesFromStageFrequency ( curves . FloodplainStage , 1 , 1 , computeIsDeterministic : true , _FailureStageDamageFunctions , ConsequenceType . Damage , false , true ) ;
143152 Threshold defaultThreshold = ComputeDefaultThreshold ( convergenceCriteria , damageFrequencyFunctions : _ImpactAreaScenarioResults . ConsequenceFrequencyFunctions . Select ( ( c ) => c . FrequencyCurve ) . ToList ( ) ) ;
144153 _ImpactAreaScenarioResults . PerformanceByThresholds . AddThreshold ( defaultThreshold ) ;
145154 }
@@ -337,9 +346,9 @@ private void ComputeIterations(ConvergenceCriteria convergenceCriteria, bool com
337346 {
338347 long computeIteration = iterationsStart + chunkIteration ;
339348
340- PairedData frequency_stage_sample = GetFrequencyStageSample ( computeIsDeterministic , computeIteration ) ;
341- PairedData systemResponse_sample = ComputeConsequencesFromStageFrequency ( frequency_stage_sample , computeIteration , chunkIteration , computeIsDeterministic ) ; //null return if no levee or no ll/damage to compute.
342- ComputePerformanceFromStageFrequency ( frequency_stage_sample , systemResponse_sample , chunkIteration ) ; // null checks the system response.
349+ FrequencyStageCurves curves = GetFrequencyStageSample ( computeIsDeterministic , computeIteration ) ;
350+ PairedData systemResponse_sample = ComputeConsequencesFromStageFrequency ( curves , computeIteration , chunkIteration , computeIsDeterministic ) ; //null return if no levee or no ll/damage to compute.
351+ ComputePerformanceFromStageFrequency ( curves , systemResponse_sample , chunkIteration ) ; // null checks the system response.
343352 } ) ;
344353 _ImpactAreaScenarioResults . ConsequenceResults . PutDataIntoHistograms ( ) ;
345354 _ImpactAreaScenarioResults . PutUncertainFrequencyCurvesIntoHistograms ( ) ;
@@ -389,7 +398,7 @@ private void ComputeIterations(ConvergenceCriteria convergenceCriteria, bool com
389398 ReportProgress ( this , new ProgressReportEventArgs ( IMPACT_AREA_SIM_COMPLETED ) ) ;
390399 }
391400
392- private PairedData GetFrequencyStageSample ( bool computeIsDeterministic , long thisComputeIteration )
401+ private FrequencyStageCurves GetFrequencyStageSample ( bool computeIsDeterministic , long thisComputeIteration )
393402 {
394403 PairedData frequency_stage_sample ;
395404 if ( _FrequencyStage . CurveMetaData . IsNull )
@@ -412,11 +421,11 @@ private PairedData GetFrequencyStageSample(bool computeIsDeterministic, long thi
412421 }
413422 if ( ! _ChannelStageFloodplainStage . IsNull )
414423 {
415- PairedData _channelstage_floodplainstage_sample = _ChannelStageFloodplainStage . SamplePairedData ( thisComputeIteration , computeIsDeterministic ) ;
416- PairedData frequency_floodplainstage = _channelstage_floodplainstage_sample . compose ( frequency_stage_sample ) ;
417- return frequency_floodplainstage ;
424+ PairedData channelstage_floodplainstage_sample = _ChannelStageFloodplainStage . SamplePairedData ( thisComputeIteration , computeIsDeterministic ) ;
425+ PairedData frequency_floodplainstage = channelstage_floodplainstage_sample . compose ( frequency_stage_sample ) ;
426+ return new FrequencyStageCurves ( ChannelStage : frequency_stage_sample , FloodplainStage : frequency_floodplainstage ) ;
418427 }
419- return frequency_stage_sample ;
428+ return new FrequencyStageCurves ( ChannelStage : frequency_stage_sample , FloodplainStage : frequency_stage_sample ) ;
420429 }
421430
422431 private PairedData GetStageFreq ( bool computeIsDeterministic , long thisComputeIteration , PairedData frequencyDischarge )
@@ -445,17 +454,17 @@ private PairedData GetStageFreq(bool computeIsDeterministic, long thisComputeIte
445454 /// <param name="thisChunkIteration"> used for saving a result in the correct place of a temp results array </param>
446455 /// <param name="computeIsDeterministic"> Monte-Carlo smapling based on iteration if false. Central tendency if true.</param>
447456 /// <returns>The sampled system response function if a levee exists, null otherwise</returns>
448- private PairedData ComputeConsequencesFromStageFrequency ( PairedData frequency_stage , long thisComputeIteration , long thisChunkIteration , bool computeIsDeterministic )
457+ private PairedData ComputeConsequencesFromStageFrequency ( FrequencyStageCurves curves , long thisComputeIteration , long thisChunkIteration , bool computeIsDeterministic )
449458 {
450459 if ( _SystemResponseFunction . CurveMetaData . IsNull )
451460 {
452461 if ( _HasFailureStageDamage )
453462 {
454- ComputeConsequencesFromStageFrequency ( frequency_stage , thisComputeIteration , thisChunkIteration , computeIsDeterministic , _FailureStageDamageFunctions , ConsequenceType . Damage ) ;
463+ ComputeConsequencesFromStageFrequency ( curves . FloodplainStage , thisComputeIteration , thisChunkIteration , computeIsDeterministic , _FailureStageDamageFunctions , ConsequenceType . Damage ) ;
455464 }
456465 if ( _HasFailureStageLifeLoss )
457466 {
458- ComputeConsequencesFromStageFrequency ( frequency_stage , thisComputeIteration , thisChunkIteration , computeIsDeterministic , _FailureStageLifeLossFunctions , ConsequenceType . LifeLoss ) ;
467+ ComputeConsequencesFromStageFrequency ( curves . FloodplainStage , thisComputeIteration , thisChunkIteration , computeIsDeterministic , _FailureStageLifeLossFunctions , ConsequenceType . LifeLoss ) ;
459468 }
460469 return null ;
461470 }
@@ -464,11 +473,11 @@ private PairedData ComputeConsequencesFromStageFrequency(PairedData frequency_st
464473 PairedData systemResponse_sample = _SystemResponseFunction . SamplePairedData ( thisComputeIteration , computeIsDeterministic ) ;
465474 if ( _HasFailureStageDamage )
466475 {
467- ComputeDamagesFromStageFrequency_WithLevee ( frequency_stage , systemResponse_sample , thisComputeIteration , thisChunkIteration , computeIsDeterministic , ConsequenceType . Damage ) ;
476+ ComputeDamagesFromStageFrequency_WithLevee ( curves , systemResponse_sample , thisComputeIteration , thisChunkIteration , computeIsDeterministic , ConsequenceType . Damage ) ;
468477 }
469478 if ( _HasFailureStageLifeLoss )
470479 {
471- ComputeDamagesFromStageFrequency_WithLevee ( frequency_stage , systemResponse_sample , thisComputeIteration , thisChunkIteration , computeIsDeterministic , ConsequenceType . LifeLoss ) ;
480+ ComputeDamagesFromStageFrequency_WithLevee ( curves , systemResponse_sample , thisComputeIteration , thisChunkIteration , computeIsDeterministic , ConsequenceType . LifeLoss ) ;
472481 }
473482 return systemResponse_sample ;
474483 }
@@ -480,15 +489,16 @@ private PairedData ComputeConsequencesFromStageFrequency(PairedData frequency_st
480489 /// <param name="frequency_stage">The stage-frequency relationship</param>
481490 /// <param name="systemResponse_sample">The sampled system response function if a levee exists, null otherwise</param>
482491 /// <param name="thisChunkIteration">Used for saving a result in the correct place of a temp results array</param>
483- private void ComputePerformanceFromStageFrequency ( PairedData frequency_stage , PairedData systemResponse_sample , int thisChunkIteration )
492+ private void ComputePerformanceFromStageFrequency ( FrequencyStageCurves curves , PairedData systemResponse_sample , int thisChunkIteration )
484493 {
485494 if ( systemResponse_sample == null || systemResponse_sample . Xvals . Count <= 2 )
486495 {
487- ComputePerformance ( frequency_stage , thisChunkIteration ) ;
496+ ComputePerformance ( curves . FloodplainStage , thisChunkIteration ) ;
488497 }
489498 else
490499 {
491- ComputeLeveePerformance ( frequency_stage , systemResponse_sample , thisChunkIteration ) ;
500+ // System response (levee fragility) is defined in channel stage, so use ChannelStage
501+ ComputeLeveePerformance ( curves . ChannelStage , systemResponse_sample , thisChunkIteration ) ;
492502 }
493503 }
494504 /// <summary>
@@ -533,7 +543,7 @@ private void ComputeConsequencesFromStageFrequency(PairedData frequency_stage, l
533543 }
534544 }
535545
536- private void ComputeDamagesFromStageFrequency_WithLevee ( PairedData frequency_stage , PairedData systemResponse , long thisComputeIteration , long thisChunkIteration , bool computeIsDeterministic , ConsequenceType type )
546+ private void ComputeDamagesFromStageFrequency_WithLevee ( FrequencyStageCurves curves , PairedData systemResponse , long thisComputeIteration , long thisChunkIteration , bool computeIsDeterministic , ConsequenceType type )
537547 {
538548 List < UncertainPairedData > failureStageDamageFunctions ;
539549 List < UncertainPairedData > nonfailureStageDamageFunctions ;
@@ -551,24 +561,37 @@ private void ComputeDamagesFromStageFrequency_WithLevee(PairedData frequency_sta
551561 }
552562
553563 PairedData validatedSystemResponse = EnsureBottomAndTopHaveCorrectProbabilities ( systemResponse ) ;
554- ComputeAnnualizedConsequence ( frequency_stage , thisComputeIteration , thisChunkIteration , computeIsDeterministic , type , failureStageDamageFunctions , validatedSystemResponse , RiskType . Fail ) ;
564+ ComputeAnnualizedConsequence ( curves , thisComputeIteration , thisChunkIteration , computeIsDeterministic , type , failureStageDamageFunctions , validatedSystemResponse , RiskType . Fail ) ;
555565
556566 //if we have nonfail damage functions, compute those too.
557567 if ( nonfailureStageDamageFunctions . Count > 0 )
558568 {
559569 PairedData complementSystemResponse = CalculateFailureProbComplement ( validatedSystemResponse ) ;
560- ComputeAnnualizedConsequence ( frequency_stage , thisComputeIteration , thisChunkIteration , computeIsDeterministic , type , nonfailureStageDamageFunctions , complementSystemResponse , RiskType . Non_Fail ) ;
570+ ComputeAnnualizedConsequence ( curves , thisComputeIteration , thisChunkIteration , computeIsDeterministic , type , nonfailureStageDamageFunctions , complementSystemResponse , RiskType . Non_Fail ) ;
561571 }
562572
563573 }
564574
565- private void ComputeAnnualizedConsequence ( PairedData frequency_stage , long thisComputeIteration , long thisChunkIteration , bool computeIsDeterministic , ConsequenceType type , List < UncertainPairedData > stageConsequenceFuncs , PairedData validatedSystemResponse , RiskType riskType )
575+ private void ComputeAnnualizedConsequence ( FrequencyStageCurves curves , long thisComputeIteration , long thisChunkIteration , bool computeIsDeterministic , ConsequenceType type , List < UncertainPairedData > stageConsequenceFuncs , PairedData validatedSystemResponse , RiskType riskType )
566576 {
567577 foreach ( UncertainPairedData stageUncertainCons in stageConsequenceFuncs )
568578 {
569579 PairedData stageDamageSample = stageUncertainCons . SamplePairedData ( thisComputeIteration , computeIsDeterministic ) ;
570- stageDamageSample = stageDamageSample . multiply ( validatedSystemResponse ) ;
571- PairedData damFreq = stageDamageSample . compose ( frequency_stage ) ; //save me for FN Plot
580+ PairedData damFreq ;
581+ if ( ReferenceEquals ( curves . ChannelStage , curves . FloodplainStage ) )
582+ {
583+ // No interior-exterior: multiply in stage domain then compose (original behavior)
584+ stageDamageSample = stageDamageSample . multiply ( validatedSystemResponse ) ;
585+ damFreq = stageDamageSample . compose ( curves . FloodplainStage ) ;
586+ }
587+ else
588+ {
589+ // Interior-exterior exists: damage uses floodplain stage, system response uses channel stage.
590+ // Compose both into frequency domain before multiplying so each uses the correct stage domain.
591+ PairedData freqDamage = stageDamageSample . compose ( curves . FloodplainStage ) ;
592+ PairedData freqPFail = validatedSystemResponse . compose ( curves . ChannelStage ) ;
593+ damFreq = freqDamage . multiply ( freqPFail ) ;
594+ }
572595
573596 // Add curve to uncertain consequence frequency curve for histogram aggregation
574597 CategoriedUncertainPairedData uncertainCurve = _ImpactAreaScenarioResults . GetOrCreateUncertainConsequenceFrequencyCurve (
@@ -691,8 +714,8 @@ public ImpactAreaScenarioResults PreviewCompute()
691714 {
692715 _ConvergenceCriteria = new ( 1 , 1 ) ;
693716 CreateEAConsequenceHistograms ( new ( 1 , 1 ) , _FailureStageDamageFunctions , ConsequenceType . Damage , RiskType . Fail ) ;
694- PairedData frequency_stage_sample = GetFrequencyStageSample ( computeIsDeterministic : true , 1 ) ;
695- ComputeConsequencesFromStageFrequency ( frequency_stage_sample , 0 , 0 , computeIsDeterministic : true , _FailureStageDamageFunctions , ConsequenceType . Damage , true , true ) ;
717+ FrequencyStageCurves curves = GetFrequencyStageSample ( computeIsDeterministic : true , 1 ) ;
718+ ComputeConsequencesFromStageFrequency ( curves . FloodplainStage , 0 , 0 , computeIsDeterministic : true , _FailureStageDamageFunctions , ConsequenceType . Damage , true , true ) ;
696719 _ImpactAreaScenarioResults . ConsequenceResults . PutDataIntoHistograms ( ) ;
697720 return _ImpactAreaScenarioResults ;
698721 }
0 commit comments