summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js
blob: 983a5c1d47e26a4720ed98a50b0b0c60d630f840 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import React from 'react'
import PropTypes from 'prop-types'
import { Bar, CartesianGrid, ComposedChart, ErrorBar, ResponsiveContainer, Scatter, XAxis, YAxis } from 'recharts'
import { AVAILABLE_METRICS, METRIC_NAMES_SHORT, METRIC_UNITS } from '../../../util/available-metrics'
import { mean, std } from 'mathjs'
import { Portfolio, Scenario } from '../../../shapes'
import approx from 'approximate-number'

const PortfolioResultsComponent = ({ portfolio, scenarios }) => {
    if (!portfolio) {
        return <div>Loading...</div>
    }

    const nonFinishedScenarios = scenarios.filter((s) => s.simulation.state !== 'FINISHED')

    if (nonFinishedScenarios.length > 0) {
        if (nonFinishedScenarios.every((s) => s.simulation.state === 'QUEUED' || s.simulation.state === 'RUNNING')) {
            return (
                <div>
                    <h1>Simulation running...</h1>
                    <p>{nonFinishedScenarios.length} of the scenarios are still being simulated</p>
                </div>
            )
        }
        if (nonFinishedScenarios.some((s) => s.simulation.state === 'FAILED')) {
            return (
                <div>
                    <h1>Simulation failed.</h1>
                    <p>
                        Try again by creating a new scenario. Please contact the OpenDC team for support, if issues
                        persist.
                    </p>
                </div>
            )
        }
    }

    const dataPerMetric = {}

    AVAILABLE_METRICS.forEach((metric) => {
        dataPerMetric[metric] = scenarios.map((scenario) => ({
            name: scenario.name,
            value: mean(scenario.results[metric]),
            errorX: std(scenario.results[metric]),
        }))
    })

    return (
        <div className="full-height" style={{ overflowY: 'scroll', overflowX: 'hidden' }}>
            <h2>Portfolio: {portfolio.name}</h2>
            <p>Repeats per Scenario: {portfolio.targets.repeatsPerScenario}</p>
            <div className="row">
                {AVAILABLE_METRICS.map((metric) => (
                    <div className="col-6 mb-2" key={metric}>
                        <h4>{METRIC_NAMES_SHORT[metric]}</h4>
                        <ResponsiveContainer aspect={16 / 9} width="100%">
                            <ComposedChart
                                data={dataPerMetric[metric]}
                                margin={{ left: 35, bottom: 15 }}
                                layout="vertical"
                            >
                                <CartesianGrid strokeDasharray="3 3" />
                                <XAxis
                                    tickFormatter={(tick) => approx(tick)}
                                    label={{ value: METRIC_UNITS[metric], position: 'bottom', offset: 0 }}
                                    type="number"
                                />
                                <YAxis dataKey="name" type="category" />
                                <Bar dataKey="value" fill="#3399FF" isAnimationActive={false} />
                                <Scatter dataKey="value" opacity={0} isAnimationActive={false}>
                                    <ErrorBar
                                        dataKey="errorX"
                                        width={10}
                                        strokeWidth={3}
                                        stroke="#FF6600"
                                        direction="x"
                                    />
                                </Scatter>
                            </ComposedChart>
                        </ResponsiveContainer>
                    </div>
                ))}
            </div>
        </div>
    )
}

PortfolioResultsComponent.propTypes = {
    portfolio: Portfolio,
    scenarios: PropTypes.arrayOf(Scenario),
}

export default PortfolioResultsComponent