---
title: Overview
source: https://infrared.city/docs/sdk/1.0/python/
---

# Python SDK

API reference for `infrared_sdk`, generated from the docstrings in the SDK source. Start with the overview below, then the module pages in the sidebar.

## A typical run

This is a complete run with your own model: one tower and a thermal comfort (UTCI) analysis. Each comment is one step of the diagram. `polygon` is a GeoJSON polygon in longitude and latitude. `coordinates` and `indices` are your mesh, in metres, in the local frame of the polygon. See [Your model](../#your-inputs) and [Coordinates](../#coordinates).

```python
import os

from infrared_sdk import InfraredClient
from infrared_sdk.analyses.types import (
AnalysesName, UtciModelBaseRequest, UtciModelRequest,
)
from infrared_sdk.models import Location, TimePeriod

# 1. Client. The key comes from the argument or from INFRARED_API_KEY.
client = InfraredClient(api_key=os.environ["INFRARED_API_KEY"])

# 2. Your model. One mesh for each building, in metres.
buildings = {"tower": {"coordinates": coordinates, "indices": indices}}

# 3. Weather. Find the nearest public station, then keep the hours you need.
period = TimePeriod(start_month=7, start_day=15, start_hour=12,
end_month=7, end_day=15, end_hour=16)
stations = client.weather.get_weather_file_from_location(lat=48.2085, lon=16.3725)
rows = client.weather.filter_weather_data(
identifier=stations[0]["uuid"], time_period=period)

# 4. Request. There is one request class for each analysis.
payload = UtciModelRequest.from_weatherfile_payload(
payload=UtciModelBaseRequest(analysis_type=AnalysesName.thermal_comfort_index),
location=Location(latitude=48.2085, longitude=16.3725),
time_period=period,
weather_data=rows,
)

# 5. Preview. This sends no job. would_bill_jobs is the number of billed jobs.
preview = client.preview_area(polygon, payload=payload, buildings=buildings)
print(preview.tile_count, preview.would_bill_jobs)

# 6. Run. One call plans, uploads, submits, waits and merges.
result = client.run_area_and_wait(payload, polygon, buildings=buildings)

# 7. Values. A float64 grid in degrees C. NaN means no value.
values = result.physical_grid()
client.close()
```

To use your own weather file, give `weather_data=parse_epw("vienna.epw")`. An analysis that needs no weather takes the request class alone, for example `SvfModelRequest(analysis_type=AnalysesName.sky_view_factors)`. See [Weather and time period](../#weather-and-time-period), [The ten analyses](../#the-ten-analyses), [Cost and retry](../#cost-and-retry) and [How a run works](../#how-a-run-works).

### Variant: no data yet

Read public buildings, trees and ground materials for the polygon. Give the objects to the run as they are, not only their inner maps: then the run can check the frame and the read margin. Buildings and ground materials need `pip install "infrared-sdk[geodata]"`. See [Public context](../#no-data-yet-public-context).

```python
buildings = client.buildings.get_area(polygon)
trees = client.vegetation.get_area(polygon)
ground = client.ground_materials.get_area(polygon)
result = client.run_area_and_wait(payload, polygon, buildings=buildings,
vegetation=trees, ground_materials=ground)
```

### Variant: facades instead of ground

Set `analysis_surfaces` to `"facades"`, `"roofs"` or `"all"`. The result gives one value for each sensor cell on your buildings, not a ground grid. Give `payload=` to the preview, or the facade job count is too low. See [Facade and roof runs](../#facade-and-roof-runs).

```python
from infrared_sdk import SvfModelRequest

payload = SvfModelRequest(analysis_type=AnalysesName.sky_view_factors,
analysis_surfaces="facades", surface_grid_size=3.0)
preview = client.preview_area(polygon, payload=payload, buildings=buildings)
result = client.run_area_and_wait(payload, polygon, buildings=buildings)
values = result.columns.physical_values()   # float64, one value for each cell
buffers = result.columns.render_buffers()   # flat arrays to draw the cells
```

### Variant: submit now, merge later

`run_area` submits the jobs and returns an `AreaSchedule` at once. Merge when the jobs are complete. `check_area_state` asks for up to 50 jobs in one request. Do not ask for each job in a loop. Give the same `known` dict each time: then the SDK does not ask again for a job that is finished. A list of requests gives one schedule for each request.

```python
import time

schedule = client.run_area(payload, polygon, buildings=buildings)
known = {}
while not client.check_area_state(schedule, known=known).is_complete:
time.sleep(10)
result = client.merge_area_jobs(schedule)
```

### Save, reload and free

Save the layout of a facade run one time for each geometry, and the values of each run: `layout.to_bytes()` and `result.columns.to_bytes()`. Reload with `FacadeLayout.from_bytes` and `SurfaceColumns.from_bytes`, then `attach_values`. Call `client.forget_schedule(schedule)` for a schedule that you will not merge, and `client.close()` when you finish. See [Save, reload and free](../#save-reload-and-free).

## Which module does what

| Page | Role | Main calls |
| --- | --- | --- |
| [Client](sdk/) | The entry point and the area runs | `InfraredClient`, `preview_area`, `run_area_and_wait`, `run_area`, `check_area_state`, `merge_area_jobs`, `forget_schedule` |
| [Analyses](analyses/) | Requests, results and job errors | `SvfModelRequest`, `UtciModelRequest`, `SurfaceColumns`, `InfraredJobError` |
| [Buildings](buildings/), [Vegetation](vegetation/), [Ground materials](ground_materials/) | Public context for an area | `client.buildings.get_area`, `client.vegetation.get_area`, `client.ground_materials.get_area` |
| [Layers (weather)](layers/) | The weather catalog and EPW files | `get_weather_file_from_location`, `filter_weather_data`, `parse_epw` |
| [Tiling](tiling/) | Tiles, schedules, previews, area results | `AreaPreview`, `AreaSchedule`, `AreaResult`, `physical_grid` |
| [Facade layout](facade_layout/) | The outline of facade and roof results | `FacadeLayout.to_bytes`, `FacadeLayout.from_bytes`, `attach_values` |
| [Legend](legend/) | Colour-scale ranges | `legend_range`, `shared_legend_range` |
| [Webhooks](webhooks/) | Calls to your server when a job ends | `client.webhooks` |

## Errors you meet

Catch these by name. The area errors and the job errors are different classes.

- `AreaRunError`: a merge cannot give a complete grid, because a tile did not contribute.
- `AreaTimeoutError`: the run passed `area_timeout`. It holds the `area_state`.
- `JobFailedError`: one job failed on the server. It is a kind of `InfraredJobError`.
- `ReadMarginError`: a public layer was read with a margin that is too narrow for the analysis.
- `EpwParseError`: your EPW file is not valid, for example it is not hourly.

See also [Cost and retry](../#cost-and-retry) for retry keys and the 402 answer when you have no credits.

Infrared SDK - Python client for the Infrared City API.
