--- name: create-problem description: >- Entry point for creating PINA problems. Routes to sub-skills based on problem type (data-driven vs physics-driven), problem class selection, domain setup, equations, conditions, and discretisation. license: MIT compatibility: opencode, codex, claude metadata: audience: users workflow: problem-creation --- # Create a PINA Problem — Entry Point > [!IMPORTANT] > Read [RULES.md](../RULES.md) before using this skill — it applies to all skills. This is the **entry-point skill** for building PINA Problems. It selects the problem type and routes to sub-skills for the deep work. ## Overview A PINA problem is a Python class that inherits from one or more of: | Base class | When to use | |-----------------------|--------------------------------------------------| | `BaseProblem` | Data-driven (supervised / unsupervised) problems | | `SpatialProblem` | PDE/ODE depending only on spatial coordinates | | `TimeDependentProblem`| Problems with a time dimension | | `ParametricProblem` | Problems with parametric dependencies | | `InverseProblem` | Problems with unknown physical parameters | Problems can **mix** base classes via multiple inheritance (e.g., `SpatialProblem` + `TimeDependentProblem` for space-time PDEs). ## Required attributes per problem type | Base class(es) | Must define | |------------------------|---------------------------------------------------| | `BaseProblem` | `input_variables`, `output_variables`, `conditions` with `input`/`target` | | `SpatialProblem` | `output_variables`, `spatial_domain`, `conditions` | | `TimeDependentProblem` | `output_variables`, `temporal_domain`, `conditions` | | `ParametricProblem` | `output_variables`, `parameter_domain`, `conditions` | | `InverseProblem` | `output_variables`, `unknown_parameter_domain`, `conditions` | ## Interactive flow ### Step 1 — Problem nature > Is your problem **data-driven** (you have input/target data) or > **physics-driven** (you have a PDE/ODE with known equations)? **Data-driven** → use `BaseProblem`. Load the **condition-setup** sub-skill for data types and conditions. **Physics-driven** → go to Step 2. If the user is unsure: - *Data-driven*: Pairs `(input, target)`, model learns to map one to the other. - *Physics-driven*: Differential equation, model minimises residual at collocation points. ### Step 2 — Select domain type(s) > Does your problem involve: > - **Spatial variables only** (e.g. `x`, `y`, `z`)? → `SpatialProblem` > - **Time** as well? → also inherit `TimeDependentProblem` > - **Parameters** that vary? → also inherit `ParametricProblem` > - **Unknown parameters** to be discovered? → also inherit `InverseProblem` Choose the base class(es) that match. ### Step 3 — Delegate to sub-skills 1. Load **define-domains** to create the domain objects (`spatial_domain`, `temporal_domain`, etc.). 2. Load **define-equations** to define PDEs/ODEs and boundary conditions. 3. Load **condition-setup** to bind equations/conditions to domains. 4. Return to this skill after conditions are defined to handle **discretisation** (see Step 7 in define-domains) and final verification. ## Templates ### Template 1: Data-driven (supervised) ```python import torch from pina import Condition, LabelTensor from pina.problem import BaseProblem input_data = LabelTensor(torch.randn(100, 1), "x") target_data = LabelTensor(torch.randn(100, 1), "y") class MySupervisedProblem(BaseProblem): input_variables = ["x"] output_variables = ["y"] conditions = { "data": Condition(input=input_data, target=target_data), } problem = MySupervisedProblem() ``` ### Template 2: Purely spatial (Poisson-like) ```python from pina.problem import SpatialProblem from pina.domain import CartesianDomain from pina import Condition from pina.equation import Equation from pina.equation.zoo import FixedValue class MySpatialProblem(SpatialProblem): output_variables = ["u"] spatial_domain = CartesianDomain({"x": [0, 1], "y": [0, 1]}) domains = { "D": spatial_domain, "boundary": spatial_domain.partial(), } conditions = { "boundary": Condition(domain="boundary", equation=FixedValue(0.0)), "D": Condition(domain="D", equation=Equation(my_pde)), } def solution(self, pts): ... ``` ### Template 3: Space-time (Burgers-like) ```python from pina.problem import SpatialProblem, TimeDependentProblem from pina.domain import CartesianDomain from pina import Condition from pina.equation import Equation from pina.equation.zoo import FixedValue class MySpaceTimeProblem(TimeDependentProblem, SpatialProblem): output_variables = ["u"] spatial_domain = CartesianDomain({"x": [-1, 1]}) temporal_domain = CartesianDomain({"t": [0, 1]}) domains = { "D": spatial_domain.update(temporal_domain), "ic": spatial_domain.update(CartesianDomain({"t": 0})), "boundary": spatial_domain.partial().update(temporal_domain), } conditions = { "boundary": Condition(domain="boundary", equation=FixedValue(0.0)), "ic": Condition(domain="ic", equation=Equation(initial_cond)), "D": Condition(domain="D", equation=Equation(my_pde)), } def solution(self, pts): ... ``` ### Template 4: Inverse problem ```python from pina.problem import SpatialProblem, InverseProblem from pina.domain import CartesianDomain from pina import Condition from pina.equation import Equation from pina.equation.zoo import FixedValue class MyInverseProblem(SpatialProblem, InverseProblem): output_variables = ["u"] spatial_domain = CartesianDomain({"x": [-2, 2], "y": [-2, 2]}) unknown_parameter_domain = CartesianDomain({"mu1": [-1, 1], "mu2": [-1, 1]}) domains = { "D": spatial_domain, "boundary": spatial_domain.partial(), } conditions = { "boundary": Condition(domain="boundary", equation=FixedValue(0.0)), "D": Condition(domain="D", equation=Equation(laplace_equation)), "data": Condition(input=input_data, target=target_data), } ``` ### Template 5: Parametric problem ```python from pina.problem import SpatialProblem, ParametricProblem from pina.domain import CartesianDomain class MyParametricProblem(SpatialProblem, ParametricProblem): output_variables = ["u"] spatial_domain = CartesianDomain({"x": [0, 1]}) parameter_domain = CartesianDomain({"mu": [0.5, 2.0]}) domains = { "D": spatial_domain.update(parameter_domain), ... } ... ``` ## Checklist - [ ] `output_variables` is a `list[str]` naming the model outputs - [ ] For data-driven: `input_variables` is a `list[str]` naming the inputs - [ ] For physics-driven: all required domains are defined (`spatial_domain`, `temporal_domain`, `parameter_domain`, or `unknown_parameter_domain` as appropriate) - [ ] `domains` dict contains a key for every domain referenced in conditions - [ ] If equation was unknown: searched the web, presented the found formulation to the user, and confirmed before using - [ ] PDE problem includes both the equation AND boundary/initial conditions - [ ] `problem.discretise_domain(n=..., mode=..., domains=...)` called for each physics domain - [ ] `problem.are_all_domains_discretised` is `True` after discretisation - [ ] (Optional) `solution(pts)` method defined with correct analytical solution returning `LabelTensor` with `output_variables` labels