Tutorial: Setting Up a PEM Project#

Overview of a PEM Project#

As introduced earlier, a PEM Project must adhere to a well-defined and standardized file system structure. The framework assumes a deterministic file system organization so that scripts can systematically discover, read, merge, and write datasets without manual intervention.

The typical project workflow is:

  1. Create the file system exactly as specified here.

  2. Populate the input directories with the required raster and vector layers for each scenario.

  3. Compute the Habitat Risk Index for the defined scenarios.

  4. Compute the Conflict Index for the defined scenarios.

  5. Compute the Benefit Index for the defined scenarios.

  6. Compute the Performance Index, integrating the previous indices.

Each stage can be executed using automated or semi-automated routines provided by the PEM scripts, ensuring reproducibility, traceability, and consistency across scenarios.

PEM Project file system layout
pem-project/                           # Project base folder
│
├── inputs/                            # Folder for all model inputs
│   │
│   ├── bathymetry.tif                 # Canonical raster (resolution, extent, CRS)
│   │
│   ├── vectors.gpkg                   # Core vector data container
│   ├── vectors.gpkg|roi               # Region of Interest (polygon layer)
│   ├── vectors.gpkg|hubs              # Land hubs (point layer)
│   ├── vectors.gpkg|habitat_benthic   # Benthic habitat map with attributes
│   ├── vectors.gpkg|habitat_pelagic   # Pelagic habitat map with attributes
│   ├── vectors.gpkg|...               # Additional optional layers
│   │
│   ├── _sources/                      # Helper folder for sourced datasets
│   │
│   ├── benefit/                       # Benefit Index data
│   │   └── benefit_users.csv          # Benefit table
│   │
│   ├── habitats/                      # Habitat rasters
│   │   ├── habitats_benthic.tif       # Rasterized Benthic habitat map
│   │   ├── habitats_benthic.csv       # Benthic habitat table
│   │   ├── habitats_pelagic.tif       # Rasterized Pelagic habitat map
│   │   ├── habitats_pelagic.csv       # Benthic habitat table
│   │   ├── benthic/                   # Benthic habitat footprints
│   │   │   ├── {habitat-group}.tif    # habitat group footprint
│   │   │   └── ...
│   │   └── pelagic/                   # Pelagic habitat footprints
│   │       ├── {habitat-group}.tif    # habitat group footprint
│   │       └── ...
│   │
│   ├── risk/                          # Habitat Risk model parameters
│   │   ├── baseline/
│   │   │   ├── benthic_info.csv       # Benthic info table for InVEST-HRA model
│   │   │   ├── benthic_scores.csv     # Benthic score table for InVEST-HRA model
│   │   │   ├── benthic_model.json     # Benthic run-file for InVEST-HRA model
│   │   │   ├── pelagic_info.csv       # Pelagic info table for InVEST-HRA model
│   │   │   ├── pelagic_scores.csv     # Pelagic score table for InVEST-HRA model
│   │   │   └── pelagic_model.json     # Benthic run-file for InVEST-HRA model
│   │   └── {scenario}/                # Alternative scenarios
│   │       └── ...
│   │
│   ├── roi/
│   │   ├── roi.shp                    # Shapefile for ROI (required by InVEST-HRA)
│   │   └── roi.tif                    # Boolean raster for ROI
│   │
│   └── users/                         # Ocean users configuration
│       ├── baseline/                  # Baseline scenario
│       │   ├── conflict.csv           # Spatial user conflict matrix
│       │   ├── oil.tif                # user raster density/intensity
│       │   ├── oil_footprint.tif      # user raster footprint
│       │   ├── fishing.tif            # (suggested users)
│       │   ├── fishing_footprint.tif  # (suggested users)
│       │   ├── {username}.tif
│       │   └── ...
│       │
│       └── {scenario}/                # Alternative scenarios
│
└── outputs/                           # Model outputs
    ├── baseline/
    │   ├── baseline_iduse.tif         # Use Performance Index
    │   ├── baseline_benefit.tif       # Benefit Index
    │   ├── baseline_conflict.tif      # Conflict Index
    │   ├── baseline_risk.tif          # Habitat Risk Index
    │   └── intermediate/              # Intermediate artifacts
    │       └── ...
    │
    └── {scenario}/                    # Alternative scenario outputs

1. Run Script: Setup the File System#

The PEM directory structure can be generated programmatically using the setup_folders() function available in the project.py module, as shown below.

This procedure ensures that the project complies exactly with the required hierarchical layout expected by the framework.

To execute the setup, the user must provide:

  • The absolute path to the project.py module;

  • The absolute path to a base directory where the project folder will be created;

  • The project name;

  • An optional list of scenario names.

Note

The baseline scenario is always generated, regardless of whether it is explicitly included in the scenarios list.

Script example
# !WARNING: run this in QGIS Python Environment
import importlib.util as iu

# define the paths to the module file
# ------------------------------------------------------
file = "path/to/project.py" # change here

# define the base folder
# ------------------------------------------------------
folder_base = "path/to/folder"  # change here

# define project name
# ------------------------------------------------------
project_name = "narnia"  # change here

# define scenario names
# ------------------------------------------------------
# change here
scenarios = [
    "baseline",
    "utopia",
    "distopia",
]

# call the function
# ------------------------------------------------------

spec = iu.spec_from_file_location("module", file)
module = iu.module_from_spec(spec)
spec.loader.exec_module(module)

output_file = module.setup_folders(
    name=project_name,
    folder_base=folder_base,
    scenarios=scenarios,
)

print(" ----- DONE -----")

2. Manually Populate Bathymetry#

The file bathymetry.tif is the canonical raster of the PEM Project. It defines the reference grid, resolution, extent, and projected CRS used throughout all spatial computations.

The user must place the file at:

{project}/inputs/bathymetry.tif

3. Populate ROI#

The ROI (Region of Interest) is a polygon vector layer delimiting the analysis domain.

Manual Input#

The user must provide the ROI layer at:

{project}/inputs/vectors.gpkg|roi

It is recommended that the ROI uses the same projected CRS as bathymetry.tif. If necessary, reprojection can be handled automatically by the framework.

Script: Setup ROI#

After the ROI layer is available, run the function setup_roi() from the project.py module. The function will ask for a set of arguments. Importantly, the bathymetry.tif canonical raster must be avaible.

Script example
# !WARNING: run this in QGIS Python Environment
import importlib.util as iu

# define the paths to the module file
# ------------------------------------------------------
file = "path/to/project.py" # change here

# define the project folder
# ------------------------------------------------------
folder = "path/to/folder" # change here

# call the function
# ------------------------------------------------------
# do not change here
spec = iu.spec_from_file_location("module", file)
module = iu.module_from_spec(spec)
spec.loader.exec_module(module)

output_files = module.setup_roi(
    folder_project=folder,
)

print(" ----- DONE -----")

4. Populate Ocean Habitats#

Ocean benthic and pelagic habitat maps are required for the Habitat Risk Index assessment and for the delineation of biophysically grounded Management Units.

During processing, habitat layers are rasterized onto the canonical grid and encoded as a categorical (qualitative) raster, where each cell represents a specific habitat group identifier.

Manual Input#

Habitats are polygon vector layers classified into:

{project}/inputs/vectors.gpkg|habitats_benthic
{project}/inputs/vectors.gpkg|habitats_pelagic

These layers are later rasterized onto the canonical grid defined by bathymetry.tif.

Technical recommendations:

  • No overlapping polygons within the same layer.

  • A unique categorical identifier (ID or code) per habitat class.

  • Prefer single-part geometries (avoid multipart features when possible).

  • Ensure spatial extent is equal to or larger than the canonical raster.

Habitat thematic detail is user-defined. The PEM framework allows aggregation into broader habitat groups at a later stage (e.g., grouping multiple mud-related classes into a single “mud” category).

Script: Setup Habitats#

After preparing habitats_benthic and/or habitats_pelagic, run the setup_habitats() function from project.py.

The function:

  • Optionally applies habitat grouping (if defined).

  • Rasterizes habitats onto the canonical grid.

  • Generates a CSV file for raster cell value interpretation.

See also

Learn how to setup habitat groups in the scripts in Tutorial: Defining Ocean Habitat Groups.

Example script:

Script example
# !WARNING: run this in QGIS Python Environment
import importlib.util as iu

# define the paths to the module file
# ------------------------------------------------------
file = "path/to/project.py" # change here

# define the project folder
# ------------------------------------------------------
folder = "path/to/folder" # change here

# define habitat code/id field
# ------------------------------------------------------
field = "code"

# define habitats grouping scheme for benthic
# ------------------------------------------------------

group_benthic = [
    {"name": "benthic_a", "values": ["MB3", "MC3"]},
    {"name": "benthic_b", "values": ["MB4", "MB5", "MB6"]},
    {"name": "benthic_b", "values": ["MC4", "MC5", "MC6"]},
    {"name": "benthic_c", "values": ["MD3"]},
    {"name": "benthic_d", "values": ["MD4", "MD5", "MD6"]},
    {"name": "benthic_e", "values": ["ME1"]},
    {"name": "benthic_f", "values": ["ME4", "MF4", "MF5"]},
    {"name": "benthic_f", "values": ["MG4", "MG6"]},
]

# define habitats grouping scheme for pelagic
# ------------------------------------------------------
group_pelagic = None  # if None, habitats are not grouped.

# call the function
# ------------------------------------------------------
# do not change here
spec = iu.spec_from_file_location("module", file)
module = iu.module_from_spec(spec)
spec.loader.exec_module(module)

ls_output = module.setup_habitats(
    folder_project=folder,
    habitat_field=field,
    groups={
        "benthic": group_benthic,
        "pelagic": group_pelagic
    },
)

print(" ----- DONE -----")

5. Populate Ocean Users#

cean Users represent spatially explicit human activities within the marine domain. These activities often overlap spatially, both within the same economic sector and across different sectors.

For each scenario, user layers are transformed into a continuous fuzzy raster that represents the relative intensity of an activity across the ocean space.

  • Relative Scaling: Every user raster intensity surface is relative to itself and to its specific scenario.

  • Value Range: Cell values are normalized, meaning the maximum values always vary between 0 and 1.

Note

Comparing Across Users and Scenarios

You can compare different Ocean Users within a scenario—or across multiple scenarios—by scaling (multiplying) the use intensity to match the user’s total benefit. This effectively maps the actual benefit distribution across the ocean space.

Example: If the total benefit of User A in a given scenario is 1000, the scaling constant for that intensity raster is calculated as:

Scaling Constant = 1000 / (Sum of all intensity cells)

Ocean User Mapping Overview#

Multiple layers geometries and formats may spatially represent a Ocean User economic sector across multiple scenarios.

Examples:

  • Offshore wind: turbines (points) and cable corridors (lines).

  • Fisheries: polygon zones and rasterized intensity maps.

  • Oil extraction: distinct layers for baseline and future expansion scenarios.

Therefore, the user may populate these different data sources accordingly.

Manual Input: Vector layers#

A vector Ocean User may be points, lines, or polygons. Layers may include numeric attributes representing intensity, density, or effort (e.g., intensity, density).

The user must populate in the vectors.gpkg database, like:

{project}/inputs/vectors.gpkg|ocean_user_a_baseline
{project}/inputs/vectors.gpkg|ocean_user_a_utopia
{project}/inputs/vectors.gpkg|ocean_user_b

Recommendations:

  • Ensure the spatial extent is equal to or larger than the canonical raster.

  • Avoid using hyphens, blank space or any non-ASCII characters in layer naming

  • Maintain consistent attribute naming for quantitative fields.

Manual Input: Raster Layers#

Raster layers for ocean users may represent:

  • Boolean footprint layers; or

  • Continuous scalar fields (e.g., intensity, density, heat maps).

The user must place raster sources under:

{project}/inputs/_sources/ocean_user_c
{project}/inputs/_sources/ocean_user_d

Recommendations:

  • Ensure the spatial extent is equal to or larger than the canonical raster.

  • Maintain consistent resolution when possible (resampling is handled during reprojection).

Script: Setup Ocean Users#

After all manual inputs for vector and raster layers, run the setup_users() function from project.py module. Note that Ocean Users must be grouped using the structured dictionary.

See also

Learn how to setup layer groups in the scripts in Tutorial: Defining Ocean Users Groups.

Script example
# !WARNING: run this in QGIS Python Environment
import importlib.util as iu

# define the paths to the module file
# ------------------------------------------------------
file = "path/to/project.py" # change here

# define the project folder
# ------------------------------------------------------
folder = "path/to/folder" # change here

# define the analysis scenario
# ------------------------------------------------------
scenario = "baseline" # change here

# define layer groups
# ------------------------------------------------------
# change "name", "field" and "weight"

# this user has both vector and raster layers
group_fisheries = {
    "vectors": [
        {"name": "fisheries_traps", "field": None, "weight": 2 },
        {"name": "fisheries_seines", "field": "intensity", "weight": 3 },
    ],
    "rasters": [
        {"name": "fisheries_gillnets.tif", "weight": 10 },
        {"name": "fisheries_longlines.tif", "weight": 5 },
    ]
}
# this user has only vector layers
group_windfarms = {
    "vectors": [
        {"name": "eolico_linhas_transmissao", "field": None, "weight": 1 },
        {"name": "eolico_torres", "field": None, "weight": 5 },
    ],
}

# this user has only raster layers
group_cargo = {
    "rasters": [
        {"name": "navegacao_densidade.tif", "weight": 1}
    ]
}
# setup groups dictionary
# ------------------------------------------------------
# define actual names for Ocean Users
groups = {
    "fisheries": group_fisheries,
    "windfarms": group_windfarms,
    "cargo": group_cargo,
}

# call the function
# ------------------------------------------------------
# do not change here
spec = iu.spec_from_file_location("module", file)
module = iu.module_from_spec(spec)
spec.loader.exec_module(module)

output_file = module.setup_users(
    folder_project=folder,
    groups=groups,
    scenario=scenario
)

print(" ----- DONE -----")