Skip to content

Commit

Permalink
Merge pull request #121 from Green-Software-Foundation/update-docs
Browse files Browse the repository at this point in the history
Update the documentation's reference links and add samples
  • Loading branch information
narekhovhannisyan authored Nov 22, 2024
2 parents 9449896 + 3fc02cc commit 6678451
Show file tree
Hide file tree
Showing 19 changed files with 184 additions and 115 deletions.
38 changes: 33 additions & 5 deletions docs/developers/how-to-build-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ b) instruct the second plugin to accept `cpu-energy` instead of the default `cpu
The `mapping` config is an object with key-value pairs, where the `key` is the 'original' parameter name that the plugin uses, and the `value` is the 'new' name that you want to use instead.
The `mapping` block is an optional and allows mapping the input and output parameters of the plugin. The structure of the `mapping` block is:

#### Mapping config and output

```yaml
name: sci
description: successful path
Expand All @@ -132,9 +134,10 @@ initialize:
method: Sci
path: 'builtin'
config:
functional-unit: requests
functional-unit: if-requests
mapping:
sci: if-sci
sci: if-sci # mapping output parameter
requests: if-requests # mapping config parameter
tree:
children:
child:
Expand All @@ -148,11 +151,36 @@ tree:
carbon-operational: 5
carbon-embodied: 0.02
carbon: 5.02
requests: 100
if-requests: 100
```

In the `outputs`, the `sci` value returned by the `Sci` plugin will be named `if-sci`.

#### Mapping input

```yaml
name: embodied-carbon demo
description:
tags:
initialize:
plugins:
embodied-carbon:
method: SciEmbodied
path: builtin
mapping:
hdd: 'hdd-mapped' # mapping input parameter
tree:
children:
child:
pipeline:
compute:
- embodied-carbon
inputs:
- timestamp: 2023-08-06T00:00
duration: 3600
hdd-mapped: 2
```

### Plugin example

Here’s a minimal example of a plugin that sums inputs based on the configuration:
Expand Down Expand Up @@ -274,7 +302,7 @@ Then, in your manifest file, provide the path in the plugin instantiation. You a
```yaml
name: plugin-demo
description: loads plugin
tags: null
tags:
initialize:
plugins:
new-plugin:
Expand Down Expand Up @@ -309,7 +337,7 @@ You should also create unit tests for your plugin to demonstrate correct executi

You can read our more advanced guide on [how to refine your plugins](./how-to-refine-plugins.md).

## Appendix: Walk-through of the Sci plugin
## Appendix: Walk-through of the Sum plugin

To demonstrate how to build a plugin that conforms to the `PluginFactory`, let's examine the `Sum` plugin.

Expand Down
22 changes: 13 additions & 9 deletions docs/developers/how-to-create-exhaust-script.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,28 @@ sidebar-position: 6

# How to create an exhaust script

The `If` framework outputs data in `yaml` format. Any other output formats require a separate script that takes the yaml output data and processes it. We provide [`if-csv`](../users/how-to-export-csv-file-with-if-csv.md) for outputting data in `csv` format bundled with IF. For any other format, you need to write an exhaust script.
The `IF` framework outputs data in `yaml` format. Any other output formats require a separate script that takes the yaml output data and processes it. We provide [`if-csv`](../users/how-to-export-csv-file-with-if-csv.md) for outputting data in `csv` format bundled with IF. For any other format, you need to write an exhaust script.
This guide will help you create your own exhaust script.

In this example, we'll create a script that executes the manifest and outputs the data in `json` format.

```ts
const IfJson = async () => {
const { manifest, output } = await parseIfCsvArgs();
const { manifest, output, params } = await parseIfCsvArgs();

if (manifest) {
const { rawManifest } = await load(manifest);
const { children } = rawManifest.tree;

if (!(children?.child || children?.['child-0']).outputs) {
throw new ManifestValidationError(FAILURE_MESSAGE_OUTPUTS);
const manifestData = await getManifestData(manifest!);
const options: CsvOptions = {
tree: manifestData.tree,
context: manifestData,
outputPath: output,
params,
};
const result = await generateCsv(options);

if (!output && result) {
console.log(result);
}

// Add logic to export the executed manifest to `json` format.
}

process.exit(0);
Expand Down
39 changes: 38 additions & 1 deletion docs/developers/how-to-refine-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sidebar-position: 2

# How to make plugins production ready

Our [How to build plugins](./how-to-build-plugins.md) guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute your plugin to one of our repositories, following these guidelines will help your PR to get merged. Even if you are not aiming to have a plugin merged into one of our repositories, consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers.
Our [How to build plugins](./how-to-build-plugins.md) guide covered the basics for how to construct an Impact Framework plugin. This guide will help you to refine your plugin to make it production-ready. These are best practice guidelines - if you intend to contribute to one of our repositories, following these guidelines will help your PR to get merged. Consistency with our norms is useful for debugging and maintaining and for making your plugin as useful as possible for other Impact Framework developers.

## 1. Naming conventions

Expand Down Expand Up @@ -79,6 +79,8 @@ throw new MissingInputDataError("my-plugin is missing my-parameter from inputs[0

### Validation

#### Input Validation

We recommend using `inputValidation` property from `PluginFactory` for validation to ensure the integrity of input data. Validate input parameters against expected types, ranges, or constraints to prevent runtime errors and ensure data consistency.

You need to use `zod` schema or `InputValidatorFunction`. Here's an example from our codebase:
Expand Down Expand Up @@ -114,6 +116,41 @@ inputValidation: z.object({
});
```

#### Config Validation

To validate the `config`, you need to use `configValidation` property from `PluginFactory`. Validate config parameters against expected types, ranges, or constraints to prevent runtime errors and ensure data consistency.

You need to use `zod` schema or `ConfigValidatorFunction`:

- When using function with `ConfigValidatorFunction` type.

```ts
configValidation: (config: ConfigParams) => {
const configSchema = z.object({
coefficient: z.preprocess(
(value) => validateArithmeticExpression('coefficient', value, 'number'),
z.number()
),
'input-parameter': z.string().min(1),
'output-parameter': z.string().min(1),
});

return validate<z.infer<typeof configSchema>>(
configSchema as ZodType<any>,
config
);
};
```

- When using `zod` schema

```ts
configValidation: z.object({
'input-parameters': z.array(z.string()),
'output-parameter': z.string().min(1),
}),
```

### Code Modularity

Break down complex functionality into smaller, manageable methods with well-defined responsibilities.
Expand Down
2 changes: 1 addition & 1 deletion docs/developers/how-to-write-unit-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ it('throws an exception on missing functional unit data.', async () => {
expect.assertions(1);

try {
await sciModel.execute(inputs);
await sciPlugin.execute(inputs);
} catch (error) {
expect(error).toBeInstanceOf(InputValidationError);
}
Expand Down
12 changes: 5 additions & 7 deletions docs/developers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ sidebar_position: 4

# Developers

This section contains information for Impact Framework developers. You are a developer if you want to *change or update* the Impact Framework by adding new features, fixing bugs or building new plugins.
This section contains information for Impact Framework developers. You are a developer if you want to _change or update_ the Impact Framework by adding new features, fixing bugs or building new plugins.

The developer documentation includes:

* [How to build plugins](./how-to-build-plugins.md)
* [How to make plugins production-ready](./how-to-refine-plugins.md)
* [How to write unit tests](./how-to-write-unit-tests.md)
* [How to visualize results](./how-to-visualize-results.md)
* [How to create exhaust scripts](./how-to-create-exhaust-script.md)

- [How to build plugins](./how-to-build-plugins.md)
- [How to make plugins production-ready](./how-to-refine-plugins.md)
- [How to write unit tests](./how-to-write-unit-tests.md)
- [How to create exhaust scripts](./how-to-create-exhaust-script.md)

If you are looking for guidance for how to use IF to measure the environmental impact of your apps, you should go to our [`user` documentation](../users/) instead.
16 changes: 6 additions & 10 deletions docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,21 @@ IF allows you to calculate the environmental impacts, such as carbon, of your so

The project is entirely open source and composability is a core design principle - we want you to be able to create your own plugins and plug them in to our framework, or pick from a broad universe of open source plugins created by others.


## Motivation
## Motivation

If you can't measure, you can't improve. Software has many negative environmental **impacts** which we need to optimize, carbon, water, and energy, to name just a few.

Unfortunately, measuring software impact metrics like carbon, water, and energy is complex and nuanced.
Unfortunately, measuring software impact metrics like carbon, water, and energy is complex and nuanced.

Modern applications are composed of many smaller pieces of software (components) running on different environments, for example, private cloud, public cloud, bare-metal, virtualized, containerized, mobile, laptops, desktops, embedded, and IoT. Many components that make up a typical software application are run on something other than resources you own or control, which makes including the impact of managed services in your measurement especially hard.
Modern applications are composed of many smaller pieces of software (components) running on different environments, for example, private cloud, public cloud, bare-metal, virtualized, containerized, mobile, laptops, desktops, embedded, and IoT. Many components that make up a typical software application are run on something other than resources you own or control, which makes including the impact of managed services in your measurement especially hard.

The impacts of software components also vary over time, so as well as understanding **which** components contribute most to the overall impacts, there is also a question of **when** they contribute the most.

Only through a granular analysis of the impacts of your software system can investments in reducing its impact be prioritized and verified. Measurement is the first and most crucial step in greening a software system, and the first step in that process with the Impact Framework is to create a tree.


## Background

This project has evolved over the two years of the GSF's existence.
This project has evolved over the two years of the GSF's existence.

During the development of the [SCI](https://github.com/Green-Software-Foundation/sci/blob/dev/SPEC.md), we acknowledged that the biggest blocker to adoption was data regarding the emissions of software components on different platforms and runtimes.

Expand All @@ -42,8 +40,7 @@ The project evolved into the [sci-guide](https://sci-guide.greensoftware.foundat

Finally, we had enough information, and [SCI case studies](https://sci-guide.greensoftware.foundation/CaseStudies) started to be written. This was a milestone moment.

But now we are in the next evolution, to have software measurement be a mainstream activity. For this to be an industry with thousands of professionals working to decarbonize software, for businesses to grow and thrive in a commercial software measurement ecosystem, we need to formalize software measurement into a discipline with standards and tooling. The SCI Specification is the standard, and the [Impact Framework](./06-specification/impact-framework.md) is the tooling.

But now we are in the next evolution, to have software measurement be a mainstream activity. For this to be an industry with thousands of professionals working to decarbonize software, for businesses to grow and thrive in a commercial software measurement ecosystem, we need to formalize software measurement into a discipline with standards and tooling. The SCI Specification is the standard, and the Impact Framework is the tooling.

## Project Structure

Expand All @@ -55,10 +52,9 @@ There are also a wide range of community-owned plugins that we make discoverable

Finally, the **source code for this documentation** website is available at the [`if-docs` Github repository](https://github.com/Green-Software-Foundation/if-docs).


## Navigating these docs

The lefthand sidebar contains links to all the information you need to understand Impact Framework.
The lefthand sidebar contains links to all the information you need to understand Impact Framework.

You can explore the key ideas underpinning Impact Framework in the [Major Concepts section](./major-concepts/index.md).

Expand Down
2 changes: 1 addition & 1 deletion docs/major-concepts/exhaust-script.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ You will get the executed manifest in the `output-sum.yaml` file.

## if-csv

The [`if-csv`](../users/how-to-export-csv-file-with-if-csv.md) script allows users to pass in `yaml` and `yml` files created using `if-run` and save the output in `csv` format. Yopu have to define the parameters you want to export from the yaml file, e.g. `energy` or `carbon`.
The [`if-csv`](../users/how-to-export-csv-file-with-if-csv.md) script allows users to pass in `yaml` and `yml` files created using `if-run` and save the output in `csv` format. You have to define the parameters you want to export from the yaml file, e.g. `energy` or `carbon`.

For the above example, you can get the following result:

Expand Down
5 changes: 4 additions & 1 deletion docs/major-concepts/if.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ The available options and their shortcuts are:
- `--help` or `-h`: prints out help instruction
- `--debug`: enables IF execution logs
- `--append`: allows you to rerun an already-computed manifest and append new values to the existing data.
- `--observe`: runs only `observe` phases of the manifest execution
- `--regroup`: runs only `regroup` phases of the manifest execution
- `--compute`: runs only `compute` phases of the manifest execution

The only required command is `--manifest`. Without a valid path to a manifest file, `if-run` has nothing to execute.

Expand All @@ -42,7 +45,7 @@ For more information on the `if-run` commands see the [CLI reference documentati

## Phased execution

To enable greener and more flexible use of IF, we separate the manifest execution into distinct phases: `observe`, `regroup` and `compute`. This is invisible to you when you run `if-run` but behind the scenes all three of these phases are being run. However, you can instruct IF to run these phases individually, to avoid recomputing parts of the manifest unnecessarily. To do this, you simply pass `--observe`, `--regroup`, and `--compute` flags to IF in the combination you need. For example, to run *only* the observe phase (to generate input data):
To enable greener and more flexible use of IF, we separate the manifest execution into distinct phases: `observe`, `regroup` and `compute`. This is invisible to you when you run `if-run` but behind the scenes all three of these phases are being run. However, you can instruct IF to run these phases individually, to avoid recomputing parts of the manifest unnecessarily. To do this, you simply pass `--observe`, `--regroup`, and `--compute` flags to IF in the combination you need. For example, to run _only_ the observe phase (to generate input data):

```
if-run -m <manifest> --observe
Expand Down
22 changes: 12 additions & 10 deletions docs/major-concepts/manifest-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Everything above the `tree` is collectively referred to as the `context`. The `t

#### Metadata

The global metadata includes the `name`, `description`, and `tags` that can be used to describe the nature of the manifest file. For example, you might name the file `Carbon Jan 2024` or similar. A short description might briefly outline the scope of the manifest file, e.g. `company x's carbon emissions due to web serves from Jab 24 - July 24`. Tags can be used to group manifest files (we do not explicitly use this field for anything currently).
The global metadata includes the `name`, `description`, and `tags` that can be used to describe the nature of the manifest file. For example, you might name the file `Carbon Jan 2024` or similar. A short description might briefly outline the scope of the manifest file, e.g. `company x's carbon emissions due to web serves from Jab 24 - July 24`. Tags is an object containing the string properties `kind`, `complexity` and `category`. It can be used to group manifest files (we do not explicitly use this field for anything currently).

#### Initialize

Expand All @@ -79,11 +79,11 @@ Impact Framework uses the `initialize` section to instantiate each plugin. A plu

There is also the option to provide a mapping to the plugin in the initialize block. Its purpose is to rename the arguments expected or returned from the plugin as part of the plugin's execution, avoiding the need to use additional plugins to rename parameters.

For example, your plugin might expect cpu/energy and your input data has the parameter cpu-energy returned from another plugin. Instead of using an additional plugin to rename the parameter and add a new one, you can use mapping to:
For example, your plugin might expect `cpu/energy` and your input data has the parameter `cpu-energy` returned from another plugin. Instead of using an additional plugin to rename the parameter and add a new one, you can use mapping to:

a) rename the output from the first plugin so that cpu/energy is returned instead of the default cpu-energy
a) rename the output from the first plugin so that `cpu/energy` is returned instead of the default `cpu-energy`

b) instruct the second plugin to accept cpu-energy instead of the default cpu/energy
b) instruct the second plugin to accept `cpu-energy` instead of the default `cpu/energy`

e.g.

Expand All @@ -102,6 +102,8 @@ initialize:

In the outputs, the `sci` value returned by the Sci plugin will be named if-sci.

[Read more on mapping](../developers/how-to-build-plugins.md#mapping)

You can also add information to the plugin's initialize section about parameter metadata if you wish to add or override the metadata hardcoded into the plugin. This is what will be reported by the `explainer` feature if you enable it. E.g.

```yaml
Expand Down Expand Up @@ -161,7 +163,7 @@ This section is autogenerated at runtime. It is a list of all the parameter meta

```yaml
explain:
sci:
sci-plugin:
inputs:
carbon:
unit: gCO2eq
Expand Down Expand Up @@ -225,7 +227,7 @@ Each component has some configuration, some input data, and a plugin pipeline.
- `defaults`: fallback values that IF defaults to if they are not present in an input observation.
- `inputs`: an array of `observation` data, with each `observation` containing usage data for a given timestep.

If a component _does not_ include its own `pipeline` or `default` values, they are inherited from the closest parent.
If a component _does not_ include its own `pipeline` or `defaults` values, they are inherited from the closest parent.

Here's an example of a moderately complex tree:

Expand All @@ -240,7 +242,7 @@ tree:
regroup:
compute:
- sum
defaults: null
defaults:
inputs:
- timestamp: 2023-07-06T00:00
duration: 10
Expand All @@ -260,7 +262,7 @@ tree:
regroup:
compute:
- sum
defaults: null
defaults:
inputs:
- timestamp: 2023-07-06T00:00
duration: 10
Expand Down Expand Up @@ -379,11 +381,11 @@ tree:
children:
my-app:
pipeline:
observe: null
observe:
regroup:
- cloud/instance-type
- cloud/region
compute: null
compute:
children:
A1:
children:
Expand Down
Loading

0 comments on commit 6678451

Please sign in to comment.