Skip to content

Cloud GPU runs

ph.cloud submits a Simulation to a remote GPU and returns the same RunResult structure as a local run. It ships in the base SDK. Numerical comparisons also require matching engines and settings.

Every recipe on this page is cloud-only. The account checks and preflight are unbilled service calls. Submission is billable. Do not run these recipes without beta credentials and authorization to spend.

First build sim with the Quickstart. Keep that construction script for later recovery. These snippets continue in the same Python session.

Match the SDK to this guide

This guide requires photonhub 0.1.4 or later. Earlier releases lack ph.Domain, ph.Mesh, ph.Port, ph.GaussianBeam, the *_rad angle names, cloud wait_timeout_s, cloud submit(max_usd=...) and the spend-safe defaults described below. Check yours with pip show photonhub and upgrade with pip install -U photonhub. See Install.

Getting access

The beta has no uninvited public signup. Request access at leapfield.ai. The SDK needs the service URL and API key issued to an authenticated beta account. An invitation code or desktop password is not an API key.

The desktop application uses a revocable session and provisions its own session-scoped cloud credential. Desktop users do not paste an API key.

Configure

Set the two environment variables to the values issued with your account. Replace the text in quotes before running this cloud-only setup recipe:

export PHOTONHUB_URL="https://YOUR_ISSUED_HOST"
export PHOTONHUB_API_KEY="YOUR_ISSUED_API_KEY"

The client reads these automatically. To configure explicitly instead:

# CLOUD ONLY: configuration and unbilled account checks
import os
import photonhub as ph

ph.cloud.configure(
    url=os.environ["PHOTONHUB_URL"],
    api_key=os.environ["PHOTONHUB_API_KEY"],
    request_timeout_s=30.0,
)
print(ph.cloud.whoami())
print(ph.cloud.account())

ph.cloud.reset() returns configuration to environment defaults. request_timeout_s bounds an HTTP request. It is separate from the job wait limit below. Keep credentials out of shared notebooks and support requests.

Choose the GPU and the solver build

During the closed test, every quote and run names the cloud GPU and the solver build. The GPU comes from the service's menu. The solver build is the one your closed-test guide names; replace the text in quotes:

# CLOUD ONLY, UNBILLED
print(ph.cloud.gpus())
GPU = "gpu:" + ph.cloud.gpus()[0]["id"]
SOLVER = "SOLVER_BUILD_FROM_YOUR_GUIDE"

Without them the service refuses the quote with HTTP 400, before any job exists, so nothing is charged.

Check before you submit

# CLOUD ONLY, UNBILLED: sim is the quickstart scene
pre = ph.cloud.preflight(sim, device=GPU, solver=SOLVER, max_usd=5.00)
print(pre.summary())
quote = ph.cloud.estimate(sim, device=GPU, solver=SOLVER)

Preflight checks device support, runnable simulation settings, the server quote and its grid, the price limit and available funds. estimate applies that grid check as well. A quote does not reserve capacity.

The check covers the quoted grid. The API service and the solver image are versioned separately. It does not establish numerical equivalence with your local engine. See Engine version skew.

Submit within a price limit

# CLOUD ONLY, BILLABLE: quotes first and refuses above the ceiling
# Choose a ceiling you are authorized to spend.
data = ph.cloud.run_quoted(sim, device=GPU, solver=SOLVER, max_usd=5.00, wait_timeout_s=300.0)

From photonhub 0.1.4, run, submit and run_quoted quote before submission by default. They bind submission to a quote within max_usd and available funds. The default ceiling is $5. A quote_id you already accepted can be bound as issued. max_usd=None submits unquoted with no ceiling. These are client controls, not a price or service-availability commitment. Check your installed version with pip show photonhub.

For asynchronous execution:

# CLOUD ONLY, BILLABLE: keep the id before waiting
job = ph.cloud.submit(sim, device=GPU, solver=SOLVER, max_usd=5.00, wait_timeout_s=300.0)
print(job.job_id)
data = job.result()

wait_timeout_s limits the client wait. CloudJobTimeout leaves the service job running and billing. Save the job id, then resume it or request cancellation with job.cancel(). If the service refuses cancellation, CloudError is raised and the job continues. A confirmed cancelled wait raises SolverRunError. A job already finished can still return its result. timeout is the older compatibility spelling and emits a warning.

Recover an existing job

Jobs survive the process that submitted them. Save the id and reuse it:

# CLOUD ONLY, UNBILLED: use an existing id, never a new submission
job = ph.cloud.resume(os.environ["PHOTONHUB_JOB_ID"], simulation=sim,
                      wait_timeout_s=300.0)
data = job.result()
print(data.provenance)

Set PHOTONHUB_JOB_ID to that existing id. Passing the original simulation is especially useful from another machine. It must be the simulation that ran. Rebuilt there from the same script it usually is: rounding is allowed for, and launch amplitudes may differ by 0.1 %, as they can between builds of the numerical libraries for a nearly degenerate port such as a square core. The profile tables of a ModeSource are compared to rounding only, so entries near a field node can be refused across machines. One that differs further, such as a launch with a flipped sign, raises SimulationMismatchError and names the difference, and the same script can build a different mesh on a newer SDK. Leave simulation= out then and the job reads with what it saved. A bundle with no sim.json is compared with the spec this client stored at submission, so without that cache only its grid is compared. The local result cache also stores state needed for user-frame coordinates and port readout. Keep it intact: on a machine without it the fields read but the ports do not come back, so transmission raises. To read them there, build the simulation on the SDK that ran the job and pass it. list_jobs() and job_status(job_id) report history, state and actual cost.

If a submission fails after the request may have reached the service, inspect job history before submitting again. Submissions are not automatically retried. A second submission creates a second billable job.

Sweeps and result structure

ph.cloud.Batch follows the local ph.Batch interface. They are separate classes. Likewise ph.submit starts a local subprocess and ph.cloud.submit creates a remote job. Cloud placement and concurrency are service-defined.

Cloud results use the local monitor names, dimensions, coordinates and normalization attributes. Inspect data.provenance["solver_version"] and data.provenance["git_sha"] to identify the engine that produced a result. Match numerical settings as well before comparing physics.

Errors

  • ValueError: construction, runnable preflight or invalid client arguments. Runnable checks occur before a request to the service.
  • SolverRunError: a simulation failed, diverged or was cancelled.
  • CloudError: configuration, authentication, service response, transport or result-transfer failure. .status_code and .body can carry service detail; .job_id, when present, identifies an accepted job to resume.
  • CloudJobTimeout: the client wait deadline expired. The job continues. HTTP request deadlines are separate; low-level timeout or I/O errors can also propagate from paths outside the cloud error wrappers.

See Troubleshooting for recovery. For beta support, contact beta@leapfield.ai.

Cost boundaries

Account, device, estimate, preflight, history, status and cancellation calls are unbilled. run, submit, run_quoted and batch execution are billable. Local CPU runs are not metered by the service.

See the full ph.cloud reference and Known limitations.