@hestia-earth/engine-models 0.73.1 → 0.73.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/Cycle.json +35 -37
- package/config/Site.json +26 -24
- package/dist/config.d.ts +1 -0
- package/dist/config.js +8 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/models.d.ts +4 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +71 -0
- package/dist/validate-config.d.ts +6 -0
- package/dist/validate-config.js +55 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/model-links.json +1342 -261
- package/package.json +2 -1
- package/search-results.json +1568 -1568
- package/src/config.ts +6 -2
- package/src/index.ts +2 -0
- package/src/models.spec.ts +1 -1
- package/src/models.ts +4 -0
- package/src/utils.spec.ts +118 -0
- package/src/utils.ts +67 -0
- package/src/validate-config.spec.ts +29 -0
- package/src/validate-config.ts +67 -0
- package/src/version.ts +1 -1
package/src/config.ts
CHANGED
|
@@ -19,13 +19,17 @@ export interface IOrchestratorConfig {
|
|
|
19
19
|
models: (IOrchestratorModelConfig | IOrchestratorModelConfig[])[];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const allowedTypes = [NodeType.Cycle, NodeType.Site, NodeType.ImpactAssessment];
|
|
23
|
-
|
|
24
22
|
export type allowedType =
|
|
25
23
|
| NodeType.Cycle
|
|
26
24
|
| NodeType.Site
|
|
27
25
|
| NodeType.ImpactAssessment;
|
|
28
26
|
|
|
27
|
+
export const allowedTypes: allowedType[] = [
|
|
28
|
+
NodeType.Cycle,
|
|
29
|
+
NodeType.Site,
|
|
30
|
+
NodeType.ImpactAssessment
|
|
31
|
+
];
|
|
32
|
+
|
|
29
33
|
const validateType = (nodeType: any) =>
|
|
30
34
|
allowedTypes.includes(nodeType) ||
|
|
31
35
|
(() => {
|
package/src/index.ts
CHANGED
package/src/models.spec.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { describe, expect, test } from '@jest/globals';
|
|
|
3
3
|
import { models } from './models';
|
|
4
4
|
|
|
5
5
|
describe('models', () => {
|
|
6
|
-
describe('
|
|
6
|
+
describe('models', () => {
|
|
7
7
|
test('has links', () => {
|
|
8
8
|
expect(models.links?.length > 0).toBeTruthy();
|
|
9
9
|
});
|
package/src/models.ts
CHANGED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
import { IModel } from './models';
|
|
4
|
+
import { findMatchingModel, findMatchingModelFromConfig } from './utils';
|
|
5
|
+
|
|
6
|
+
describe('models', () => {
|
|
7
|
+
describe('findMatchingModel', () => {
|
|
8
|
+
const model = 'stehfestBouwman2006';
|
|
9
|
+
const term = 'noxToAirInorganicFertiliser';
|
|
10
|
+
const path =
|
|
11
|
+
'hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py';
|
|
12
|
+
|
|
13
|
+
test('should find by model', () => {
|
|
14
|
+
const params = { model, term } as Partial<IModel>;
|
|
15
|
+
|
|
16
|
+
expect(findMatchingModel(params)).toEqual(
|
|
17
|
+
expect.objectContaining({ model, term, path })
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('should handle values with dot', () => {
|
|
22
|
+
expect(
|
|
23
|
+
findMatchingModel(
|
|
24
|
+
{
|
|
25
|
+
model: 'ipcc2019',
|
|
26
|
+
term: 'animal.pregnancyRateTotal'
|
|
27
|
+
},
|
|
28
|
+
{ useDotValues: true }
|
|
29
|
+
)
|
|
30
|
+
).toEqual(
|
|
31
|
+
expect.objectContaining({
|
|
32
|
+
model: 'ipcc2019',
|
|
33
|
+
term: 'pregnancyRateTotal'
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('findMatchingModelFromConfig', () => {
|
|
40
|
+
test('should handle completeness', () => {
|
|
41
|
+
const model = 'cycle';
|
|
42
|
+
|
|
43
|
+
expect(
|
|
44
|
+
findMatchingModelFromConfig({
|
|
45
|
+
model,
|
|
46
|
+
value: 'completeness'
|
|
47
|
+
})
|
|
48
|
+
).toEqual(
|
|
49
|
+
expect.objectContaining({
|
|
50
|
+
model: 'cycle',
|
|
51
|
+
modelKey: 'completeness.animalFeed'
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('should handle feedConversionRatio', () => {
|
|
57
|
+
const model = 'hestia';
|
|
58
|
+
|
|
59
|
+
expect(
|
|
60
|
+
findMatchingModelFromConfig({
|
|
61
|
+
model,
|
|
62
|
+
value: 'feedConversionRatio'
|
|
63
|
+
})
|
|
64
|
+
).toEqual(
|
|
65
|
+
expect.objectContaining({
|
|
66
|
+
model: 'hestia',
|
|
67
|
+
term: 'feedConversionRatioCarbon'
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('should handle ecoinventV3AndEmberClimate', () => {
|
|
73
|
+
const model = 'ecoinventV3AndEmberClimate';
|
|
74
|
+
|
|
75
|
+
expect(
|
|
76
|
+
findMatchingModelFromConfig({
|
|
77
|
+
model,
|
|
78
|
+
value: 'all'
|
|
79
|
+
})
|
|
80
|
+
).toEqual(
|
|
81
|
+
expect.objectContaining({
|
|
82
|
+
model
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('should handle ecoinventV3AndEmberClimate', () => {
|
|
88
|
+
const model = 'ecoinventV3AndEmberClimate';
|
|
89
|
+
|
|
90
|
+
expect(
|
|
91
|
+
findMatchingModelFromConfig({
|
|
92
|
+
model,
|
|
93
|
+
value: 'all'
|
|
94
|
+
})
|
|
95
|
+
).toEqual(
|
|
96
|
+
expect.objectContaining({
|
|
97
|
+
model
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('should handle seed_emissions', () => {
|
|
103
|
+
const model = 'hestia';
|
|
104
|
+
|
|
105
|
+
expect(
|
|
106
|
+
findMatchingModelFromConfig({
|
|
107
|
+
model,
|
|
108
|
+
value: 'seed_emissions'
|
|
109
|
+
})
|
|
110
|
+
).toEqual(
|
|
111
|
+
expect.objectContaining({
|
|
112
|
+
model,
|
|
113
|
+
modelKey: 'seed_emissions'
|
|
114
|
+
})
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { IOrchestratorModelConfig } from './config';
|
|
2
|
+
import { IModel, models } from './models';
|
|
3
|
+
|
|
4
|
+
type findMatchFunc = (
|
|
5
|
+
model: IModel,
|
|
6
|
+
key: string,
|
|
7
|
+
value: string | string[]
|
|
8
|
+
) => boolean;
|
|
9
|
+
|
|
10
|
+
const matchWithDot: findMatchFunc = (model, key, value) =>
|
|
11
|
+
typeof value === 'string' && value.split('.').pop() === model[key];
|
|
12
|
+
|
|
13
|
+
const matchPath: findMatchFunc = (model, key, value) =>
|
|
14
|
+
key === 'path' && typeof value === 'string' && model[key].startsWith(value);
|
|
15
|
+
|
|
16
|
+
export const findMatchingModel = (
|
|
17
|
+
model: Partial<IModel>,
|
|
18
|
+
config: {
|
|
19
|
+
useDotValues?: boolean;
|
|
20
|
+
} = {
|
|
21
|
+
useDotValues: false
|
|
22
|
+
}
|
|
23
|
+
): IModel =>
|
|
24
|
+
Object.keys(model).length > 0
|
|
25
|
+
? models.links.find((m) =>
|
|
26
|
+
Object.entries(model).every(
|
|
27
|
+
([key, value]) =>
|
|
28
|
+
value === m[key] ||
|
|
29
|
+
(config.useDotValues && matchWithDot(m, key, value)) ||
|
|
30
|
+
matchPath(m, key, value)
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
: null;
|
|
34
|
+
|
|
35
|
+
const modelMatchOrder = ({
|
|
36
|
+
model,
|
|
37
|
+
value
|
|
38
|
+
}: Pick<IOrchestratorModelConfig, 'model' | 'value'>) =>
|
|
39
|
+
[
|
|
40
|
+
{ model, term: value },
|
|
41
|
+
{ model, modelKey: value },
|
|
42
|
+
{
|
|
43
|
+
model,
|
|
44
|
+
modelKey: value.split('.').pop()
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
model,
|
|
48
|
+
term: value.split('.').pop()
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
model,
|
|
52
|
+
path: ['hestia_earth', 'models', model, value].join('/')
|
|
53
|
+
},
|
|
54
|
+
!value || value === 'all' ? { model } : null
|
|
55
|
+
].filter(Boolean) as Partial<IModel>[];
|
|
56
|
+
|
|
57
|
+
export const findMatchingModelFromConfig = (
|
|
58
|
+
model: Pick<IOrchestratorModelConfig, 'model' | 'value'>
|
|
59
|
+
) =>
|
|
60
|
+
modelMatchOrder(model)
|
|
61
|
+
.map(
|
|
62
|
+
(params) =>
|
|
63
|
+
findMatchingModel(params) ||
|
|
64
|
+
findMatchingModel(params, { useDotValues: true })
|
|
65
|
+
)
|
|
66
|
+
?.filter(Boolean)
|
|
67
|
+
?.shift();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, test } from '@jest/globals';
|
|
2
|
+
import { NodeType } from '@hestia-earth/schema';
|
|
3
|
+
|
|
4
|
+
import { validateConfig } from './validate-config';
|
|
5
|
+
|
|
6
|
+
describe('validate-config', () => {
|
|
7
|
+
describe('validateConfig', () => {
|
|
8
|
+
describe(NodeType.Cycle, () => {
|
|
9
|
+
test('should be valid', () => {
|
|
10
|
+
const results = validateConfig(NodeType.Cycle);
|
|
11
|
+
expect(results).toEqual([]);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe(NodeType.ImpactAssessment, () => {
|
|
16
|
+
test('should be valid', () => {
|
|
17
|
+
const results = validateConfig(NodeType.ImpactAssessment);
|
|
18
|
+
expect(results).toEqual([]);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe(NodeType.Site, () => {
|
|
23
|
+
test('should be valid', () => {
|
|
24
|
+
const results = validateConfig(NodeType.Site);
|
|
25
|
+
expect(results).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { allowedType, IOrchestratorModelConfig, loadConfig } from './config';
|
|
2
|
+
import { findMatchingModelFromConfig } from './utils';
|
|
3
|
+
|
|
4
|
+
// models not included in the links
|
|
5
|
+
const skipModels = ['emissions.deleted', 'transformations'];
|
|
6
|
+
|
|
7
|
+
const isHestiaResidueCyclicModel = ({
|
|
8
|
+
model,
|
|
9
|
+
value
|
|
10
|
+
}: IOrchestratorModelConfig) =>
|
|
11
|
+
model === 'hestia' && value?.startsWith('residue');
|
|
12
|
+
|
|
13
|
+
const isOrganicCarbonCyclicModel = ({
|
|
14
|
+
model,
|
|
15
|
+
value
|
|
16
|
+
}: IOrchestratorModelConfig) =>
|
|
17
|
+
model === 'hestia' && value?.startsWith('organicCarbonPer');
|
|
18
|
+
|
|
19
|
+
const includeModel = (model: IOrchestratorModelConfig) =>
|
|
20
|
+
[
|
|
21
|
+
!skipModels.includes(model.model),
|
|
22
|
+
// cyclic dependencies
|
|
23
|
+
!isHestiaResidueCyclicModel(model),
|
|
24
|
+
!isOrganicCarbonCyclicModel(model)
|
|
25
|
+
].every(Boolean);
|
|
26
|
+
|
|
27
|
+
// dependencies that cannot be validated
|
|
28
|
+
const skipDependencies = [
|
|
29
|
+
// completeness gap-fills after other models run
|
|
30
|
+
'completeness'
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const matchDependencies =
|
|
34
|
+
(models: IOrchestratorModelConfig[]) => (model: IOrchestratorModelConfig) => {
|
|
35
|
+
const { dependencies } = findMatchingModelFromConfig(model);
|
|
36
|
+
const previousModels = models.slice(0, models.indexOf(model));
|
|
37
|
+
const existing =
|
|
38
|
+
dependencies?.filter((value) =>
|
|
39
|
+
previousModels.some(
|
|
40
|
+
(config) => config.value?.split('.').pop() === value
|
|
41
|
+
)
|
|
42
|
+
) ?? [];
|
|
43
|
+
return {
|
|
44
|
+
model,
|
|
45
|
+
existing,
|
|
46
|
+
missing:
|
|
47
|
+
dependencies
|
|
48
|
+
// skip dependencies that dont exist at all
|
|
49
|
+
?.filter((value) => models.some((config) => config.value === value))
|
|
50
|
+
?.filter(
|
|
51
|
+
(v) =>
|
|
52
|
+
!existing.includes(v) &&
|
|
53
|
+
!skipDependencies.includes(v) &&
|
|
54
|
+
v !== model.value
|
|
55
|
+
) ?? []
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const validateConfig = (nodeType: allowedType) => {
|
|
60
|
+
const config = loadConfig(nodeType);
|
|
61
|
+
// flatten all models to process one by one, starting with the last one
|
|
62
|
+
const models = config.models.flat();
|
|
63
|
+
|
|
64
|
+
const results = models.filter(includeModel).map(matchDependencies(models));
|
|
65
|
+
|
|
66
|
+
return results.filter((r) => r.missing.length > 0);
|
|
67
|
+
};
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const ENGINE_VERSION = '0.73.
|
|
1
|
+
export const ENGINE_VERSION = '0.73.3';
|