Skip to content

Authoring Problemtypes (Python) ​

Problemtypes can be written in Python instead of JavaScript — the API is the same, spelled snake_case. Drop a .py file in .kratos/problemtypes/ and it appears in the Problemtype dropdown.

Complete worked examples: faithful Python ports of the five built-in problemtypes live in example/problemtypes/ (structural.py, fluid.py, convection_diffusion.py, potential_flow.py, shallow_water.py). A parity test keeps them byte-identical to the TypeScript originals, so they are always a current, runnable reference — copy one and start editing.

Python problemtypes run inside Pyodide (Python compiled to WebAssembly, bundled with the extension) — they do not use your system Python and cannot import Kratos or touch the filesystem. They only describe the case; the generated ProjectParameters.json runs with your real Kratos install like any other case. The interpreter (~14 MB of WASM) loads lazily the first time a .py problemtype is discovered, so there is no cost when you don't use the feature.

A complete example ​

python
# .kratos/problemtypes/my_thermal.py
from kratos_problemtype import define_problemtype, section, field, condition, material_law

def solver_settings(values, ctx):
    # ctx is a dict with snake_case keys — see the table below.
    return {
        "solver_type": "transient",
        "analysis_type": "linear",
        "model_part_name": ctx["model_part_name"],
        "domain_size": ctx["domain_size"],
        "model_import_settings": {"input_type": "mdpa", "input_filename": ctx["mdpa_stem"]},
        "material_import_settings": {"materials_filename": ctx["materials_file_name"]},
        "echo_level": values["echoLevel"],
        "problem_domain_sub_model_part_list": ctx["parts_model_parts"],
        "processes_sub_model_part_list": ctx["skin_model_parts"],
        "time_stepping": {"time_step": values["timeStep"]},
    }

define_problemtype(
    id="myThermal",
    name="My Thermal (py)",
    description="Custom transient heat transfer",
    analysis_stage="KratosMultiphysics.ConvectionDiffusionApplication.convection_diffusion_analysis",
    model_part_name="ThermalModelPart",
    materials_file_name="ThermalMaterials.json",
    domain_sizes=[2, 3],
    sections=[
        section("problem", "Problem data",
                field("timeStep", "Time step", "number", default=0.1),
                field("endTime", "End time", "number", default=1.0),
                field("echoLevel", "Echo level", "int", default=1)),
    ],
    parts_condition="parts",
    conditions=[
        condition("parts", "Thermal body", list="list_other_processes",
                  target="volume", process_template={}),
        condition("temperature", "Fixed temperature",
                  list="constraints_process_list", target="any",
                  fields=[field("value", "T [K]", "number", default=293.15)],
                  process_template={
                      "python_module": "assign_scalar_variable_process",
                      "kratos_module": "KratosMultiphysics",
                      "process_name": "AssignScalarVariableProcess",
                      "Parameters": {
                          "model_part_name": "$path",
                          "variable_name": "TEMPERATURE",
                          "constrained": True,
                          "value": "$field:value",
                          "interval": [0.0, "End"],
                      },
                  }),
    ],
    material_laws=[
        material_law("thermal", "", variables=[
            field("DENSITY", "ρ", "number", default=1000),
            field("CONDUCTIVITY", "k", "number", default=0.6),
            field("SPECIFIC_HEAT", "c", "number", default=4184),
        ]),
    ],
    output={"nodal_defaults": ["TEMPERATURE"]},
    solver_settings=solver_settings,        # required hook
    # build_process=..., post_process=..., main_script=...   (optional hooks)
)

API surface ​

python
field(id, label, type, default=None, options=None, visible_when=None,
      unit=None, help=None, group=None, min=None, max=None, step=None,
      advanced=None, ref_collection=None) # unit: e.g. "kg/m³" — see below
field_group(id, label, icon=None, collapsed=None)
section(id, label, *fields, groups=None, icon=None)
condition(id, label, list="constraints_process_list", target="any",
          fields=(), process_template=None, help=None, category=None,
          icon=None, no_process=None, emit=None)
domain(id, label, model_part_name, mdpa_suffix, parts_condition,
       condition_ids, materials_file_name=None, required=None,
       mesh_naming=None)                  # one physics of a coupled problemtype
process(python_module, process_name=None, kratos_module="KratosMultiphysics",
        parameters=None)                  # process_template sugar
material_law(id, name, variables=(), domain_size=None, domain=None,
             application=None, group=None, parameters=None, when=None)
entry_type(id, label, *fields, parts=None, icon=None, help=None)
collection(id, label, *entry_types, min=None, max=None, defaults=None,
           icon=None, help=None)          # a repeatable list of named entries
INTERVAL_TOTAL                            # the [0.0, "End"] interval constant
define_problemtype(id, name, analysis_stage, model_part_name,
                   materials_file_name, domain_sizes,
                   sections=(), conditions=(), material_laws=(),
                   parts_condition=None, mesh_naming=None, output=None,
                   description=None, icon=None, family=None,
                   solver_settings=None,   # required
                   build_process=None, post_process=None, main_script=None,
                   domains=None, extra_files=None, validate=None,
                   collections=None, applications=None,
                   coupling_interfaces=None, primary_document_name=None,
                   driver_script_name=None, driver_template=None)
application(name, when=None)              # a required Kratos application

mesh_naming declares the element/condition block names the solver expects — when the mesh differs, Generate writes a renamed <stem>_case.mdpa copy (final name = <base><dim>D<nnodes>N):

python
mesh_naming={"elements": "$field:elementBase",           # or "Element"
             "conditions": {2: "LineLoadCondition", 3: "SurfaceLoadCondition"}}

When no single base can describe a formulation's names (shells, cables, springs), a kind takes a {"field": "<enum field>", "names": {<value>: <entry>}} table, where an entry is a template ("SpringDamperElement{dim}D"), one exact name ("CableElement3D2N") or a {dim: {nodes: name}} table of exact names; a value with no entry keeps the base meaning, and a shape a table omits makes Generate refuse that block instead of inventing a Kratos type.

icon names a toolbar icon for the problemtype's forms (the built-ins use ptStructural / ptFluid / ptThermal / ptPotentialFlow / ptShallowWater). A condition's list may also be a custom process-list name (e.g. boundary_conditions_process_list).

unit declares the unit a field's value is entered in (field("DENSITY", "ρ", "number", default=1000, unit="kg/m³")). It is what material presets convert into, so a row published in g/cm³ fills a variable declared in kg/m³. Without it the unit is inferred from the brackets in label and then from a well-known Kratos variable name; a value whose unit cannot be established on either side is only accepted as an exact match, never passed through unconverted. See Units and material presets for the recognised quantities.

visible_when accepts one rule dict or a list of them (all must hold); a rule uses "equals" or "one_of". Layout (group, category, family) and domains behave as described in the JavaScript guide. Collections are declared with collection() and entry_type(); a ref field names its collection with ref_collection="responses", and a caseValue field may only appear inside an entry type (both are checked when the file loads). The hooks see ctx["collections"][id], the entries with defaults merged and their parts as dotted modelParts. In a coupled problemtype the hooks receive ctx["domains"][id] with that domain's model_part_name, mdpa_stem, assignments, materials and parts_model_parts. coupling_interfaces, primary_document_name, driver_script_name, driver_template and applications mean what their JavaScript counterparts mean (coupled problemtypes, required applications); application("MeshMovingApplication", when={"field": "aleMesh", "equals": True}) adds a conditional one. An extra_files hook may return a file's content as a dict or list instead of a string: the host writes it as JSON formatted like the primary document, which is how example/problemtypes/coupled.py writes the CoSimulation participant documents byte-identical to the built-in's.

Field types, $path / $root / $field:<id> template placeholders, process lists and the generated document shape are identical to the JavaScript API. options accepts plain strings (options=["a", "b"]) or dicts ({"value": "a", "label": "A"}).

process() builds a process_template dict and derives process_name from the CamelCased python_module when omitted (the Kratos convention — assign_scalar_variable_process → AssignScalarVariableProcess):

python
process_template=process(
    "assign_scalar_variable_process",
    parameters={"model_part_name": "$path", "variable_name": "TEMPERATURE",
                "value": "$field:value", "interval": INTERVAL_TOTAL})

Validation is eager: a wrong field type, an unknown process list/target, a duplicate id, or a parts_condition that names no condition raises ValueError naming the offending id while the file loads — the broken problemtype shows up as a disabled dropdown entry carrying that message instead of failing later at generate time.

Note: field ids and the keys of values stay exactly as you declare them (the built-ins use camelCase like timeStep) — only the API argument names and the ctx keys are snake_case.

Hooks ​

HookSignatureDefault when omitted / returning None
solver_settings(values, ctx) → dict— required
build_process(cond, assignment, ctx) → dict | Noneresolve the condition's process_template
post_process(project_parameters, ctx) → dictunchanged document
main_script(ctx) → strthe standard MainKratos.py

All hook arguments and return values are plain JSON data (dicts, lists, numbers, strings) — no Kratos objects.

The ctx dict ​

KeyContents
mdpa_stemmdpa file name without extension
domain_size2 or 3
model_part_name, materials_file_namefrom the declaration
valuesall section field values flattened
assignments, materialsthe raw user assignments
parts_model_partsdotted names of the Parts assignments
skin_model_partsdotted names of every non-Parts assignment
sub_model_partsall SubModelPart paths in the mesh
collectionscollection id → entries {name, type, parts, values, modelParts} (only when the problemtype declares collections)

Released under the GNU AGPL-3.0-or-later License.