Select Git revision
calculations.svelte.ts
calculations.svelte.ts 31.64 KiB
import {
getRolePersonsIdKey,
type GroupEntity,
type PopulationWithoutId,
} from "@openfisca/json-model"
import { v4 as uuidV4 } from "uuid"
import {
type Evaluation,
type EvaluationByName,
nonVirtualVariablesName,
nonVirtualVariablesNameByReformName,
updateEvaluations,
waterfalls,
} from "$lib/decompositions"
import { entityByKey } from "$lib/entities"
import { metadata } from "$lib/metadata"
import type { ParametricReform } from "$lib/reforms"
import {
billActive,
billName,
revaluationName,
shared,
year,
yearPLF,
} from "$lib/shared.svelte"
import {
type Axis,
type Calculation,
type CalculationByName,
getPopulationReservedKeys,
type Situation,
type SituationWithAxes,
type TestCasesCalculationInput,
} from "$lib/situations"
import {
budgetEditableParametersName,
type BudgetVariable,
budgetVariableNameByVariableName,
budgetVariablesConfig,
budgetVariablesName,
otherCalculatedVariablesName,
summaryCalculatedVariablesName,
variableSummaryByName,
variableSummaryByNameByReformName,
type VariableValue,
type VariableValueByCalculationName,
type VariableValues,
} from "$lib/variables"
export type CalculationName = "amendment" | "bill" | "law" | "revaluation"
export const calculationNames: CalculationName[] = [
"law",
"revaluation",
"bill",
"amendment",
]
export interface RequestedCalculations {
budgetCalculationNames?: Set<CalculationName>
budgetVariableName?: string
situationIndexByCalculationName: RequestedSituationIndexByCalculationName
}
export type RequestedSituationIndexByCalculationName = Partial<{
[calculationName in CalculationName]: number | null // `null` means "every test cases"
}>
export const requestedCalculations = $state({
situationIndexByCalculationName: {},
}) as RequestedCalculations
let budgetSimulationAbortController = new AbortController()
const testCasesAbortControllers: {
[calculationName: string]: AbortController
} = Object.fromEntries(
calculationNames.map((calculationName) => [
calculationName,
new AbortController(),
]),
)
export function buildBudgetDemandBody(
variableName: string,
outputVariables: string[],
quantileBaseVariable: string[],
quantileCompareVariables: string[],
includeDisplay = true,
) {
const budgetParametricReform = Object.fromEntries(
Object.entries(shared.parametricReform).filter(([parameterName]) =>
budgetEditableParametersName.has(parameterName),
),
)
const metadataLite = { ...metadata }
metadataLite.distributions = {}
return {
amendement: budgetParametricReform,
base: yearPLF,
displayMode: includeDisplay ? shared.displayMode : undefined,
metadata: metadataLite,
output_variables: outputVariables,
quantile_base_variable: quantileBaseVariable,
quantile_compare_variables: quantileCompareVariables,
winners_loosers_variable: variableName,
quantile_nb: 10,
plf: billName === undefined ? undefined : yearPLF,
}
}
export async function calculateBudget(
budgetVariableName: string,
// budgetCalculationNames: Set<CalculationName>,
): Promise<void> {
budgetSimulationAbortController.abort()
budgetSimulationAbortController = new AbortController()
if (!budgetVariablesName.has(budgetVariableName)) {
console.error(
`Budget calculation for variable ${budgetVariableName} is not available`,
)
shared.budgetSimulation = undefined
return
}
const variableName = budgetVariableNameByVariableName[budgetVariableName]
const variableConfig: BudgetVariable = budgetVariablesConfig[variableName]
const body = JSON.stringify(
buildBudgetDemandBody(
variableName,
variableConfig.outputVariables,
variableConfig.quantileBaseVariable,
variableConfig.quantileCompareVariables,
),
)
shared.budgetSimulation = undefined
const response = await fetch("/budgets", {
body,
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=utf-8",
},
method: "POST",
signal: budgetSimulationAbortController.signal,
})
if (!response.ok) {
console.error(
`"/budgets": Error while calculating budget:\n${body}\n${response.status} ${response.statusText}`,
)
console.error(await response.text())
shared.budgetSimulation = {
errors: [
`Une erreur inattendue (${response.status} ${response.statusText}) s'est produite et les impacts budgétaires ne sont pas disponibles pour le moment. Écrivez-nous à leximpact@assemblee-nationale.fr.`,
],
isPublic: true,
}
shared.budgetSimulation = undefined
return
}
shared.budgetSimulation = await response.json()
}
export function calculateTestCases(
situationIndexByCalculationName: RequestedSituationIndexByCalculationName,
) {
const aggregatedSituation: SituationWithAxes = {}
const axesData: { situationIndex?: number; variables: string[] } =
shared.axes.reduce(
(
result: { situationIndex?: number; variables: string[] },
parallelAxes,
) => {
for (const axis of parallelAxes) {
result.situationIndex = axis.situationIndex
if (result.variables.includes(axis.name)) {
continue
}
result.variables.push(axis.name)
}
return result
},
{ variables: [] },
)
const entities = Object.values(entityByKey)
if (
Object.values(situationIndexByCalculationName).some(
(situationIndex) => situationIndex === null,
)
) {
let personIndex = -1
// Some of the calculations are requested to be done for every situations.
// Aggregate every situations into a single one without calculated variables.
for (const [situationIndex, situation] of shared.testCases.entries()) {
const inputInstantsByVariableName =
shared.inputInstantsByVariableNameArray[situationIndex]
const situationPrefix = `Cas type n°${situationIndex + 1} | `
for (const entity of entities) {
const entitySituation = situation[entity.key_plural as string]
if (entitySituation === undefined) {
continue
}
let aggregatedEntitySituation =
aggregatedSituation[entity.key_plural as string]
if (aggregatedEntitySituation === undefined) {
aggregatedEntitySituation = aggregatedSituation[
entity.key_plural as string
] = {}
}
const reservedKeys = getPopulationReservedKeys(entity)
for (const [populationId, population] of Object.entries(
entitySituation,
).sort(([populationId1], [populationId2]) =>
populationId1.localeCompare(populationId2),
)) {
const transformedPopulation: PopulationWithoutId = {}
if (!entity.is_person) {
for (const role of (entity as GroupEntity).roles) {
const personsIdKey = getRolePersonsIdKey(role)
const personsId = population[personsIdKey] as string[] | undefined
if (personsId === undefined) {
continue
}
transformedPopulation[personsIdKey] = personsId.map(
(personId) => situationPrefix + personId,
)
}
} else {
personIndex++
}
for (const [variableName, variableValueByInstant] of Object.entries(
population,
)) {
if (
reservedKeys.has(variableName) ||
(entity.is_person &&
axesData.situationIndex === personIndex &&
axesData.variables.includes(variableName))
) {
continue
}
const inputVariableValueByInstant: {
[instant: string]: VariableValue | null
} = {}
const inputInstants =
inputInstantsByVariableName[variableName] ?? new Set<string>()
for (const [instant, variableValue] of Object.entries(
variableValueByInstant as {
[instant: string]: VariableValue | null
},
)) {
if (!inputInstants.has(instant)) {
// Ignore calculated value.
continue
}
inputVariableValueByInstant[instant] = variableValue
}
if (Object.keys(inputVariableValueByInstant).length > 0) {
transformedPopulation[variableName] = inputVariableValueByInstant
}
}
aggregatedEntitySituation[situationPrefix + populationId] =
transformedPopulation
}
}
}
if (shared.axes.length > 0) {
aggregatedSituation.axes = shared.axes
}
}
const message = {
period: year.toString(),
}
const newCalculationByName: CalculationByName = {}
const lawSituationIndex = situationIndexByCalculationName.law
if (lawSituationIndex !== undefined) {
const calculation: Calculation = (newCalculationByName.law = {
running: true,
})
let situation: SituationWithAxes
if (lawSituationIndex === null) {
situation = aggregatedSituation
} else {
calculation.situationIndex = lawSituationIndex
situation = cleanSituation(
shared.testCases[lawSituationIndex],
shared.axes,
)
}
calculation.input = {
...message,
situation,
variables: [
...summaryCalculatedVariablesName,
...otherCalculatedVariablesName,
...nonVirtualVariablesName,
],
}
sendTestCasesSimulation("law", calculation.input) // Don't wait for result.
}
const revaluationSituationIndex = situationIndexByCalculationName.revaluation
if (
revaluationSituationIndex !== undefined &&
revaluationName !== undefined
) {
const calculation: Calculation = (newCalculationByName.revaluation = {
running: true,
})
let situation: SituationWithAxes
if (revaluationSituationIndex === null) {
situation = aggregatedSituation
} else {
calculation.situationIndex = revaluationSituationIndex
situation = cleanSituation(
shared.testCases[revaluationSituationIndex],
shared.axes,
)
}
calculation.input = {
...message,
reform: revaluationName,
situation,
variables: [
...summaryCalculatedVariablesName,
...otherCalculatedVariablesName,
...(nonVirtualVariablesNameByReformName[revaluationName] ??
nonVirtualVariablesName),
],
}
sendTestCasesSimulation("revaluation", calculation.input) // Don't wait for result.
}
const billSituationIndex = situationIndexByCalculationName.bill
if (billSituationIndex !== undefined && billActive) {
const calculation: Calculation = (newCalculationByName.bill = {
running: true,
})
let situation: SituationWithAxes
if (billSituationIndex === null) {
situation = aggregatedSituation
} else {
calculation.situationIndex = billSituationIndex
situation = cleanSituation(
shared.testCases[billSituationIndex],
shared.axes,
)
}
calculation.input = {
...message,
reform: billName,
situation,
variables: [
...summaryCalculatedVariablesName,
...otherCalculatedVariablesName,
...(nonVirtualVariablesNameByReformName[billName] ??
nonVirtualVariablesName),
],
}
sendTestCasesSimulation("bill", calculation.input) // Don't wait for result.
}
const amendmentSituationIndex = situationIndexByCalculationName.amendment
if (amendmentSituationIndex !== undefined) {
if (Object.keys(shared.parametricReform).length === 0) {
// Remove amendment evaluations from decompositions.
shared.evaluationByNameArray = shared.evaluationByNameArray.map(
(evaluationByName, situationIndex): EvaluationByName => {
const updatedEvaluationByName = Object.fromEntries(
Object.entries(evaluationByName).map(
([variableName, evaluation]) => {
const calculationEvaluationByName = {
...evaluation.calculationEvaluationByName,
}
delete calculationEvaluationByName["amendment"]
return [
variableName,
{ ...evaluation, calculationEvaluationByName },
]
},
),
)
return updateEvaluations(
shared.decompositionByName,
updatedEvaluationByName,
shared.testCases[situationIndex].slider?.vectorIndex ?? 0,
shared.vectorLength,
waterfalls,
)
},
)
// Remove amendment values.
shared.valuesByCalculationNameByVariableNameArray =
shared.valuesByCalculationNameByVariableNameArray.map(
(valuesByCalculationNameByVariableName) =>
Object.fromEntries(
Object.entries(valuesByCalculationNameByVariableName).map(
([variableName, valuesByCalculationName]) => {
const updatedValuesByCalculationName = {
...valuesByCalculationName,
}
delete updatedValuesByCalculationName["amendment"]
return [variableName, updatedValuesByCalculationName]
},
),
),
)
} else {
const calculation: Calculation = (newCalculationByName.amendment = {
running: true,
})
let situation: SituationWithAxes
if (amendmentSituationIndex === null) {
situation = aggregatedSituation
} else {
calculation.situationIndex = amendmentSituationIndex
situation = cleanSituation(
shared.testCases[amendmentSituationIndex],
shared.axes,
)
}
calculation.input = {
...message,
parametric_reform: shared.parametricReform,
reform: billName ?? revaluationName, // Will be removed when billName and revaluationName are undefined.
situation,
variables: [
...summaryCalculatedVariablesName,
...otherCalculatedVariablesName,
...(nonVirtualVariablesNameByReformName[billName as string] ??
nonVirtualVariablesNameByReformName[revaluationName as string] ??
nonVirtualVariablesName),
],
}
sendTestCasesSimulation("amendment", calculation.input) // Don't wait for result.
}
}
shared.calculationByName = newCalculationByName
}
export function calculateTestCasesAdditionalVariables(
additionalVariablesName: Set<string>,
): void {
const newCalculationByName = { ...shared.calculationByName }
for (const [calculationName, calculation] of Object.entries(
newCalculationByName,
)) {
if (calculation.input === undefined) {
continue
}
let variablesAdded = false
const variablesName = [...calculation.input.variables]
for (const variableName of additionalVariablesName) {
if (!variablesName.includes(variableName)) {
variablesName.push(variableName)
variablesAdded = true
}
}
if (variablesAdded) {
const input = { ...calculation.input, variables: variablesName }
newCalculationByName[calculationName as CalculationName] = {
...calculation,
input,
running: true,
}
sendTestCasesSimulation(calculationName as CalculationName, input) // Don't wait for result.
}
}
shared.calculationByName = newCalculationByName
}
function cleanSituation(
situationToClean: Situation,
axes: Axis[][],
): SituationWithAxes {
const situation: SituationWithAxes = {}
for (const entity of Object.values(entityByKey)) {
const entitySituation = situationToClean[entity.key_plural as string]
if (entitySituation === undefined) {
continue
}
situation[entity.key_plural as string] = entitySituation
}
if (axes.length > 0) {
situation.axes = axes.map((parallelAxes) =>
parallelAxes.map((axis) => ({
...axis,
index: (axis.index as number) - (axis.situationIndex as number),
situationIndex: 0,
})),
)
}
return situation
}
export function isNullVariableValueByCalculationName(
variableValueByCalculationName: VariableValueByCalculationName,
): boolean {
return Object.values(variableValueByCalculationName).every(
(variableValue) => variableValue === undefined || variableValue === 0,
)
}
export function requestAllBudgetCalculations(variableName: string): void {
if (
variableName !== requestedCalculations.budgetVariableName ||
requestedCalculations.budgetCalculationNames === undefined ||
requestedCalculations.budgetCalculationNames.size < calculationNames.length
) {
requestedCalculations.budgetCalculationNames = new Set(calculationNames)
requestedCalculations.budgetVariableName = variableName
}
}
export function requestAllTestCasesCalculations(
requestedSituationIndex: number | null,
): void {
if (
calculationNames.some((calculationName) => {
const situationIndex =
requestedCalculations.situationIndexByCalculationName[calculationName]
return (
situationIndex !== null && situationIndex !== requestedSituationIndex
)
})
) {
calculateTestCases(
Object.fromEntries(
calculationNames.map((calculationName) => {
const situationIndex =
requestedCalculations.situationIndexByCalculationName[
calculationName
]
return [
calculationName,
situationIndex === null ? null : requestedSituationIndex,
]
}),
),
)
}
}
export function requestBudgetCalculation(
variableName: string,
calculationName: CalculationName,
): void {
const budgetCalculationNames =
variableName === requestedCalculations.budgetVariableName
? (requestedCalculations.budgetCalculationNames ??
new Set<CalculationName>())
: new Set<CalculationName>()
if (!budgetCalculationNames.has(calculationName)) {
requestedCalculations.budgetCalculationNames =
budgetCalculationNames.add(calculationName)
requestedCalculations.budgetVariableName = variableName
}
}
export function requestTestCasesCalculation(
calculationName: CalculationName,
requestedSituationIndex: number | null,
): void {
const situationIndex =
requestedCalculations.situationIndexByCalculationName[calculationName]
if (situationIndex !== null && situationIndex !== requestedSituationIndex) {
requestedCalculations.situationIndexByCalculationName[calculationName] =
requestedSituationIndex
}
}
export async function sendBudgetSimulationDemand() {
const budgetVariableName = shared.displayMode?.parametersVariableName
if (
budgetVariableName === undefined ||
!budgetVariablesName.has(budgetVariableName)
) {
console.error(
`Budget calculation for variable ${budgetVariableName} is not available`,
)
shared.budgetSimulation = undefined
return
}
const variableName = budgetVariableNameByVariableName[budgetVariableName]
const variableConfig: BudgetVariable = budgetVariablesConfig[variableName]
const simulationBody = buildBudgetDemandBody(
variableName,
variableConfig.outputVariables,
variableConfig.quantileBaseVariable,
variableConfig.quantileCompareVariables,
false,
)
const urlString = "/budgets/demands"
const res = await fetch(urlString, {
body: JSON.stringify(
{
displayMode: shared.displayMode,
email: shared.requestedSimulationEmail,
request: Object.fromEntries(
Object.entries(shared.parametricReform).filter(([parameterName]) =>
budgetEditableParametersName.has(parameterName),
),
),
simulation: simulationBody,
},
null,
2,
),
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=utf-8",
},
method: "POST",
})
if (!res.ok) {
shared.requestedSimulationEmail = undefined
console.error(
`Error ${
res.status
} while sending a simulation request at ${urlString}\n\n${await res.text()}`,
)
return
}
shared.requestedSimulationEmail = undefined
}
async function sendTestCasesSimulation(
calculationName: CalculationName,
{
period,
parametric_reform,
reform,
situation,
variables,
}: TestCasesCalculationInput,
) {
const completeVariableSummaryByName =
billName === undefined
? variableSummaryByName
: variableSummaryByNameByReformName[billName]
try {
// Note: crypto.randomUUID() is not supported by Safari before version 15.4.
// const token = crypto.randomUUID()
const token = uuidV4()
testCasesAbortControllers[calculationName].abort()
testCasesAbortControllers[calculationName] = new AbortController()
const response = await fetch("/test_cases", {
body: JSON.stringify({
period,
parametric_reform,
reform,
situation,
title: calculationName,
token,
variables,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=utf-8",
},
method: "POST",
signal: testCasesAbortControllers[calculationName].signal,
})
if (!response.ok) {
console.error(
`Error while submitting test case simulation:\n${response.status} ${response.statusText}`,
)
console.error(await response.text())
return
}
const { evaluations } = (await response.json()) as {
evaluations: [
{
entity: string
name: string
value: VariableValues
},
]
token: string
}
let calculation = shared.calculationByName[calculationName] as Calculation
const updatedEvaluationByNameArray =
calculation.situationIndex === undefined
? shared.evaluationByNameArray
: [...shared.evaluationByNameArray]
// Use an intermediate variable to avoir multiple updates of shared.valuesByCalculationNameByVariableNameArray
// in following loop.
let valuesByCalculationNameByVariableNameArray =
shared.valuesByCalculationNameByVariableNameArray
for (const {
entity: entityKey,
name: variableName,
value,
} of evaluations) {
// Round returned values if unit of variable is a currency.
const variable = completeVariableSummaryByName[variableName]
const roundedValue =
variable.unit === "currency" || variable.unit?.startsWith("currency-")
? value.map((valueAtIndex) => Math.round(valueAtIndex as number))
: value
const entity = entityByKey[entityKey]
if (calculation.situationIndex === undefined) {
// Variable has been computed for all test cases.
// Count total population of test cases.
let testCasesPopulationCount = 0
for (const situation of shared.testCases) {
const entitySituation = situation[entity.key_plural as string] ?? {}
const situationPopulationCount = Object.keys(entitySituation).length
testCasesPopulationCount += situationPopulationCount
}
// Split evaluation.value vector for each situation.
{
let testCasesPopulationIndex = 0
for (const [
situationIndex,
valuesByCalculationNameByVariableName,
] of valuesByCalculationNameByVariableNameArray.entries()) {
const situation = shared.testCases[situationIndex]
const entitySituation = situation[entity.key_plural as string] ?? {}
const values: VariableValues = []
for (
let index = testCasesPopulationIndex, vectorIndex = 0;
vectorIndex < shared.vectorLength;
index += testCasesPopulationCount, vectorIndex++
) {
for (const situationPersonIndex of Object.keys(
entitySituation,
).keys()) {
values.push(roundedValue[index + situationPersonIndex])
}
}
testCasesPopulationIndex += Object.keys(entitySituation).length
;(valuesByCalculationNameByVariableName[variableName] ??= {})[
calculationName
] = values
}
}
} else {
// Variable has been computed for a single test case.
const updatedValuesByCalculationNameByVariableNameArray = [
...valuesByCalculationNameByVariableNameArray,
]
const valuesByCalculationNameByVariableName = {
...updatedValuesByCalculationNameByVariableNameArray[
calculation.situationIndex
],
}
const valuesByCalculationName =
valuesByCalculationNameByVariableName[variableName] ?? {}
valuesByCalculationNameByVariableName[variableName] = {
...valuesByCalculationName,
[calculationName]: roundedValue,
}
updatedValuesByCalculationNameByVariableNameArray[
calculation.situationIndex
] = valuesByCalculationNameByVariableName
valuesByCalculationNameByVariableNameArray =
updatedValuesByCalculationNameByVariableNameArray
}
// Update evaluations for decompositions.
const calculationNonVirtualVariablesName =
calculationName === "law"
? nonVirtualVariablesName
: (nonVirtualVariablesNameByReformName[billName as string] ??
nonVirtualVariablesName)
if (calculationNonVirtualVariablesName.includes(variableName)) {
if (calculation.situationIndex === undefined) {
// Variable has been computed for all test cases.
// First, update delta and values of evaluations.
for (const [
situationIndex,
evaluationByName,
] of updatedEvaluationByNameArray.entries()) {
const situation = shared.testCases[situationIndex]
const values = valuesByCalculationNameByVariableNameArray[
situationIndex
][variableName][calculationName] as VariableValues
const entitySituation = situation[entity.key_plural as string] ?? {}
const situationPopulationCount = Object.keys(entitySituation).length
const delta = new Array(shared.vectorLength).fill(0)
for (const situationPersonIndex of Object.keys(
entitySituation,
).keys()) {
for (
let index = situationPersonIndex, vectorIndex = 0;
vectorIndex < shared.vectorLength;
index += situationPopulationCount, vectorIndex++
) {
delta[vectorIndex] += values[index]
}
}
if (evaluationByName[variableName] === undefined) {
evaluationByName[variableName] = {}
}
if (
evaluationByName[variableName].calculationEvaluationByName ===
undefined
) {
evaluationByName[variableName].calculationEvaluationByName = {}
}
if (
evaluationByName[variableName].calculationEvaluationByName[
calculationName
] === undefined
) {
evaluationByName[variableName].calculationEvaluationByName[
calculationName
] = {}
}
evaluationByName[variableName].calculationEvaluationByName[
calculationName
]!.delta = delta
evaluationByName[variableName].calculationEvaluationByName[
calculationName
]!.deltaAtVectorIndex =
delta[situation.slider?.vectorIndex ?? 0] ?? 0
evaluationByName[variableName].fromOpenFisca = true
}
} else {
// Variable has been computed for a single test case.
// First, update delta and values of evaluations.
const situation = shared.testCases[calculation.situationIndex]
const values = valuesByCalculationNameByVariableNameArray[
calculation.situationIndex
][variableName][calculationName] as VariableValues
const entitySituation = situation[entity.key_plural as string] ?? {}
const situationPopulationCount = Object.keys(entitySituation).length
const delta = new Array(shared.vectorLength).fill(0)
for (const situationPersonIndex of Object.keys(
entitySituation,
).keys()) {
for (
let index = situationPersonIndex, vectorIndex = 0;
vectorIndex < shared.vectorLength;
index += situationPopulationCount, vectorIndex++
) {
delta[vectorIndex] += values[index]
}
}
const evaluationByName = {
...updatedEvaluationByNameArray[calculation.situationIndex],
}
if (evaluationByName[variableName] === undefined) {
evaluationByName[variableName] = {}
}
if (
evaluationByName[variableName].calculationEvaluationByName ===
undefined
) {
evaluationByName[variableName].calculationEvaluationByName = {}
}
if (
evaluationByName[variableName].calculationEvaluationByName[
calculationName
] === undefined
) {
evaluationByName[variableName].calculationEvaluationByName[
calculationName
] = {}
}
evaluationByName[variableName].calculationEvaluationByName[
calculationName
]!.delta = delta
evaluationByName[variableName].calculationEvaluationByName[
calculationName
]!.deltaAtVectorIndex = delta[situation.slider?.vectorIndex ?? 0] ?? 0
evaluationByName[variableName].fromOpenFisca = true
}
}
}
shared.valuesByCalculationNameByVariableNameArray =
valuesByCalculationNameByVariableNameArray
// Update deltaSums of evaluations from their new delta.
if (calculation.situationIndex === undefined) {
shared.evaluationByNameArray = updatedEvaluationByNameArray.map(
(evaluationByName, situationIndex) =>
updateEvaluations(
shared.decompositionByName,
evaluationByName,
shared.testCases[situationIndex].slider?.vectorIndex ?? 0,
shared.vectorLength,
waterfalls,
),
)
} else {
updatedEvaluationByNameArray[calculation.situationIndex] =
updateEvaluations(
shared.decompositionByName,
updatedEvaluationByNameArray[calculation.situationIndex],
shared.testCases[calculation.situationIndex].slider?.vectorIndex ?? 0,
shared.vectorLength,
waterfalls,
)
shared.evaluationByNameArray = updatedEvaluationByNameArray
}
calculation = { ...calculation }
delete calculation.running
shared.calculationByName[calculationName] = calculation
} catch (e) {
if (e?.name === "AbortError") {
return
}
}
}
export function variableValueByCalculationNameFromEvaluation(
evaluation: Evaluation | undefined | null,
revaluationName: string | undefined | null,
billName: string | undefined | null,
parametricReform: ParametricReform,
): VariableValueByCalculationName {
return {
amendment:
Object.keys(parametricReform).length === 0
? undefined
: Math.abs(
evaluation?.calculationEvaluationByName["amendment"]
?.deltaAtVectorIndex ?? 0,
),
bill:
billName == null
? undefined
: Math.abs(
evaluation?.calculationEvaluationByName["bill"]
?.deltaAtVectorIndex ?? 0,
),
law: Math.abs(
evaluation?.calculationEvaluationByName["law"]?.deltaAtVectorIndex ?? 0,
),
revaluation:
revaluationName == null
? undefined
: Math.abs(
evaluation?.calculationEvaluationByName["revaluation"]
?.deltaAtVectorIndex ?? 0,
),
}
}