11import React , { useMemo , useState } from "react" ;
22import { useQueries } from "@tanstack/react-query" ;
3- import { CircularProgress , NativeSelect , Typography } from "@equinor/eds-core-react" ;
3+ import { Accordion , CircularProgress , NativeSelect , Typography } from "@equinor/eds-core-react" ;
44import { getGridSimulationResult , ResultIsPending } from "@/api/api" ;
55import { MainContainer } from "@/components/styles" ;
66import { GridSimulationResult } from "@/dto/GridSimulation" ;
77import LineChart , { LineSeries } from "@/components/LineChart" ;
8- import { collectOutputSubstances , pointOutput , significantSubstances } from "@/functions/GridSimulation" ;
8+ import {
9+ collectOutputSubstances ,
10+ pointOutput ,
11+ significantSubstances ,
12+ visiblePhaseKinds ,
13+ } from "@/functions/GridSimulation" ;
914import { optionName } from "@/functions/Substance" ;
15+ import { useAvailableModels } from "@/contexts/ModelContext" ;
16+ import { buildModelSections } from "@/utils/modelUtils" ;
1017
1118interface CompareGridSimulationsProps {
1219 gridIds : string [ ] ;
@@ -18,7 +25,88 @@ const modelChainLabel = (result: GridSimulationResult): string => {
1825 return firstSim . input . models . map ( ( m ) => m . modelId ) . join ( " → " ) || "Unknown model" ;
1926} ;
2027
28+ function phaseLabel ( kind : string ) : string {
29+ return kind === "aqueous" ? "Aqueous" : "CO2-rich" ;
30+ }
31+
32+ interface CompareSectionProps {
33+ results : GridSimulationResult [ ] ;
34+ modelIndex : number ;
35+ phaseKind : string ;
36+ }
37+
38+ const CompareSection : React . FC < CompareSectionProps > = ( { results, modelIndex, phaseKind } ) => {
39+ const allSubstances = useMemo ( ( ) => {
40+ const substances = new Set < string > ( ) ;
41+ results . forEach ( ( r ) =>
42+ significantSubstances ( r . simulations , modelIndex , phaseKind ) . forEach ( ( s ) => substances . add ( s ) )
43+ ) ;
44+ if ( substances . size === 0 ) {
45+ results . forEach ( ( r ) =>
46+ collectOutputSubstances ( r . simulations , modelIndex , phaseKind ) . forEach ( ( s ) => substances . add ( s ) )
47+ ) ;
48+ }
49+ return Array . from ( substances ) . sort ( ) ;
50+ } , [ results , modelIndex , phaseKind ] ) ;
51+
52+ const [ substance , setSubstance ] = useState < string > ( "" ) ;
53+ const selectedSubstance = substance || allSubstances [ 0 ] || "" ;
54+
55+ const xAxisSubstance = results [ 0 ] ?. axes [ 0 ] ?. substance ?? "" ;
56+ const unifiedXValues = Array . from (
57+ new Set ( results . flatMap ( ( r ) => r . simulations . map ( ( sim ) => sim . input . concentrations [ xAxisSubstance ] ?? 0 ) ) )
58+ ) . sort ( ( a , b ) => a - b ) ;
59+
60+ const series : LineSeries [ ] = results . map ( ( r ) => {
61+ const valueToSim = new Map ( r . simulations . map ( ( sim ) => [ sim . input . concentrations [ xAxisSubstance ] ?? 0 , sim ] ) ) ;
62+ return {
63+ label : `${ modelChainLabel ( r ) } · ${ r . axes [ 0 ] ?. substance ?? "" } ` ,
64+ data : unifiedXValues . map ( ( x ) => {
65+ const sim = valueToSim . get ( x ) ;
66+ return sim ? pointOutput ( sim , selectedSubstance , modelIndex , phaseKind ) : null ;
67+ } ) ,
68+ } ;
69+ } ) ;
70+
71+ const axisSubstances = new Set ( results . map ( ( r ) => r . axes [ 0 ] ?. substance ) . filter ( Boolean ) ) ;
72+ const xAxisLabel = axisSubstances . size === 1 ? `${ [ ...axisSubstances ] [ 0 ] } (ppm)` : "Varied concentration (ppm)" ;
73+ const unit = phaseKind === "aqueous" ? "wt%" : "ppm" ;
74+
75+ return (
76+ < >
77+ < NativeSelect
78+ id = { `compare-${ modelIndex } -${ phaseKind } ` }
79+ label = "Output substance"
80+ value = { selectedSubstance }
81+ onChange = { ( e ) => setSubstance ( e . target . value ) }
82+ style = { { maxWidth : "400px" , marginBottom : "1rem" } }
83+ >
84+ { allSubstances . map ( ( s ) => (
85+ < option key = { s } value = { s } >
86+ { optionName ( s ) }
87+ </ option >
88+ ) ) }
89+ </ NativeSelect >
90+ { selectedSubstance === "" ? (
91+ < Typography variant = "body_short" italic >
92+ No output substances available to compare.
93+ </ Typography >
94+ ) : (
95+ < LineChart
96+ xValues = { unifiedXValues }
97+ series = { series }
98+ xAxisLabel = { xAxisLabel }
99+ yAxisLabel = { `${ selectedSubstance } (${ unit } )` }
100+ aspectRatio = { 2 }
101+ />
102+ ) }
103+ </ >
104+ ) ;
105+ } ;
106+
21107const CompareGridSimulations : React . FC < CompareGridSimulationsProps > = ( { gridIds } ) => {
108+ const { models } = useAvailableModels ( ) ;
109+
22110 const queries = useQueries ( {
23111 queries : gridIds . map ( ( id ) => ( {
24112 queryKey : [ "grid-simulation" , id ] ,
@@ -32,18 +120,6 @@ const CompareGridSimulations: React.FC<CompareGridSimulationsProps> = ({ gridIds
32120 const hasError = queries . some ( ( q ) => q . isError ) ;
33121 const results = queries . map ( ( q ) => q . data ) . filter ( ( data ) : data is GridSimulationResult => data !== undefined ) ;
34122
35- const allSubstances = useMemo ( ( ) => {
36- const substances = new Set < string > ( ) ;
37- results . forEach ( ( r ) => significantSubstances ( r . simulations ) . forEach ( ( s ) => substances . add ( s ) ) ) ;
38- if ( substances . size === 0 ) {
39- results . forEach ( ( r ) => collectOutputSubstances ( r . simulations ) . forEach ( ( s ) => substances . add ( s ) ) ) ;
40- }
41- return Array . from ( substances ) . sort ( ) ;
42- } , [ results ] ) ;
43-
44- const [ substance , setSubstance ] = useState < string > ( "" ) ;
45- const selectedSubstance = substance || allSubstances [ 0 ] || "" ;
46-
47123 const header = (
48124 < Typography variant = "h2" style = { { marginBottom : "2rem" } } >
49125 Compare Grid Simulations
@@ -77,24 +153,51 @@ const CompareGridSimulations: React.FC<CompareGridSimulationsProps> = ({ gridIds
77153 ) ;
78154 }
79155
80- const xAxisSubstance = results [ 0 ] ?. axes [ 0 ] ?. substance ?? "" ;
81- const unifiedXValues = Array . from (
82- new Set ( results . flatMap ( ( r ) => r . simulations . map ( ( sim ) => sim . input . concentrations [ xAxisSubstance ] ?? 0 ) ) )
83- ) . sort ( ( a , b ) => a - b ) ;
156+ const firstSim = results [ 0 ] ?. simulations [ 0 ] ;
157+ const inputModels = firstSim ?. input . models ?? [ ] ;
158+ const sections = buildModelSections ( inputModels , models ) ;
84159
85- const series : LineSeries [ ] = results . map ( ( r ) => {
86- const valueToSim = new Map ( r . simulations . map ( ( sim ) => [ sim . input . concentrations [ xAxisSubstance ] ?? 0 , sim ] ) ) ;
87- return {
88- label : `${ modelChainLabel ( r ) } · ${ r . axes [ 0 ] ?. substance ?? "" } ` ,
89- data : unifiedXValues . map ( ( x ) => {
90- const sim = valueToSim . get ( x ) ;
91- return sim ? pointOutput ( sim , selectedSubstance ) : null ;
92- } ) ,
93- } ;
160+ const allPhasesByModel = new Map < number , string [ ] > ( ) ;
161+ sections . forEach ( ( section ) => {
162+ section . indices . forEach ( ( modelIndex ) => {
163+ const phases = new Set < string > ( ) ;
164+ results . forEach ( ( r ) => {
165+ visiblePhaseKinds ( r . simulations , modelIndex ) . forEach ( ( k ) => phases . add ( k ) ) ;
166+ } ) ;
167+ const order = [ "co2-rich" , "aqueous" ] ;
168+ allPhasesByModel . set (
169+ modelIndex ,
170+ order . filter ( ( k ) => phases . has ( k ) )
171+ ) ;
172+ } ) ;
94173 } ) ;
95174
96- const axisSubstances = new Set ( results . map ( ( r ) => r . axes [ 0 ] ?. substance ) . filter ( Boolean ) ) ;
97- const xAxisLabel = axisSubstances . size === 1 ? `${ [ ...axisSubstances ] [ 0 ] } (ppm)` : "Varied concentration (ppm)" ;
175+ const allAccordions : { key : string ; header : string ; modelIndex : number ; phaseKind : string } [ ] = [ ] ;
176+ sections . forEach ( ( section ) => {
177+ section . indices . forEach ( ( modelIndex ) => {
178+ const phases = allPhasesByModel . get ( modelIndex ) ?? [ ] ;
179+ const modelName =
180+ models . find ( ( m ) => m . modelId === inputModels [ modelIndex ] ?. modelId ) ?. displayName ??
181+ inputModels [ modelIndex ] ?. modelId ;
182+ if ( phases . length === 1 ) {
183+ allAccordions . push ( {
184+ key : `${ section . category } -${ modelIndex } ` ,
185+ header : `${ section . category } : ${ modelName } ` ,
186+ modelIndex,
187+ phaseKind : phases [ 0 ] ,
188+ } ) ;
189+ } else {
190+ phases . forEach ( ( phaseKind ) => {
191+ allAccordions . push ( {
192+ key : `${ section . category } -${ modelIndex } -${ phaseKind } ` ,
193+ header : `${ section . category } : ${ modelName } — ${ phaseLabel ( phaseKind ) } ` ,
194+ modelIndex,
195+ phaseKind,
196+ } ) ;
197+ } ) ;
198+ }
199+ } ) ;
200+ } ) ;
98201
99202 return (
100203 < MainContainer >
@@ -103,32 +206,23 @@ const CompareGridSimulations: React.FC<CompareGridSimulationsProps> = ({ gridIds
103206 Comparing { results . length } grid simulations. Each line shows how the selected output substance responds
104207 across the configured range.
105208 </ Typography >
106- < NativeSelect
107- id = "compareGridSubstance"
108- label = "Output substance"
109- value = { selectedSubstance }
110- onChange = { ( e ) => setSubstance ( e . target . value ) }
111- style = { { maxWidth : "400px" , marginBottom : "1rem" } }
112- >
113- { allSubstances . map ( ( s ) => (
114- < option key = { s } value = { s } >
115- { optionName ( s ) }
116- </ option >
209+
210+ < div style = { { display : "flex" , flexDirection : "column" , gap : "1rem" } } >
211+ { allAccordions . map ( ( item , index ) => (
212+ < Accordion key = { item . key } >
213+ < Accordion . Item isExpanded = { index === allAccordions . length - 1 } >
214+ < Accordion . Header > { item . header } </ Accordion . Header >
215+ < Accordion . Panel >
216+ < CompareSection
217+ results = { results }
218+ modelIndex = { item . modelIndex }
219+ phaseKind = { item . phaseKind }
220+ />
221+ </ Accordion . Panel >
222+ </ Accordion . Item >
223+ </ Accordion >
117224 ) ) }
118- </ NativeSelect >
119- { selectedSubstance === "" ? (
120- < Typography variant = "body_short" italic >
121- No output substances available to compare.
122- </ Typography >
123- ) : (
124- < LineChart
125- xValues = { unifiedXValues }
126- series = { series }
127- xAxisLabel = { xAxisLabel }
128- yAxisLabel = { `${ selectedSubstance } (ppm)` }
129- aspectRatio = { 2 }
130- />
131- ) }
225+ </ div >
132226 </ MainContainer >
133227 ) ;
134228} ;
0 commit comments