.. _jython-impl:

# Jython

By default, RiskScape will execute your Python function using the Java-based implementation, called [Jython](https://www.jython.org).

Alternatively, you can setup RiskScape to use :ref:`cpython-impl`.
You can also mix and match, and use CPython for some functions and Jython for other functions.

This page describes some of the behaviour specific to Jython in more detail.

## Calling other RiskScape functions

You can call other user-defined Jython functions from your function, as well as some built-in RiskScape functions.
For example, we could call our `hello` function from the :ref:`hello world example <hello_world>` from _another_ different function:

```python
  # basic syntax is: functions.get(<function-name>).call(<arguments>)
  greeting = functions.get('hello').call('Kia ora', 'Ronnie')
```

Note that currently this has the following limitations:
- The types of the arguments must match exactly,
e.g. you cannot pass an integer to something that expects a floating argument.
- You must not omit any optional arguments.
- Built-in RiskScape functions that support a wide range of argument-types (i.e. functions that accept or return the type `Anything`) are not supported.

## Jython packages

If you have used Python (or more specifically, CPython) in the past, you may notice that Jython is a little different,
in particular when importing and using packages.

For example, generating a random number between zero and one would usually look like the following in CPython.

```python
import random
num = random.uniform(0, 1.0)
```

Whereas in Jython, it would look like this:

```python
from java.util import Random
random = Random()
num = random.nextDouble()
```

.. note::
  We recommend you call the built-in RiskScape functions where possible,
  rather than importing Jython packages.

For example, to achieve the same thing using the built-in RiskScape function:

```python
num = functions.get('random_uniform').call(0.0, 1.0)
```

.. tip::
    In general, once you start needing to import Python packages,
    it will be simpler to switch from using Jython to using :ref:`cpython-impl`.

## Basic loss function

Now let's look at a more complicated example.
The below Python code (`quake.py`) is an example of a basic loss function.

```python
def function(building, hazard):
  loss = {}
  loss['replacement'] = building['replace']
  loss['hazard'] = hazard
  construction = building['construct']
  if hazard is None:
    loss['dr'] = 0
    loss['loss'] = 0
  else:
    loss['dr'] = damage_function(construction, hazard)
    loss['loss'] = loss['replacement'] * loss['dr']
  return loss

CONSTRUCT_WOOD = 1
  
def damage_function(construction, hazard):
  if construction == CONSTRUCT_WOOD:
    mean = 0.22
    stddev = 0.74
  else:
    mean = 0.92
    stddev = 0.64

  # call the built-in 'lognorm_cdf' riskscape function
  return functions.get('lognorm_cdf').call(hazard, mean, stddev)  
```

This function demonstrates many points we have already covered, such as:

* The `building` argument is a `struct`, and contains two attributes that we are interested in: construction type and replacement value.
  Note that the actual building input data file might contain many more attributes, but our function only cares about two of them. 
* The `hazard` argument is _nullable_, i.e. it may be `None`.
* We call the builtin RiskScape function `lognorm_cdf` to calculate the cumulative log normal distribution.
* The return value is a `struct`, which we build up as a Python dictionary.

The INI file definition for this function might look like this:

```ini
[function quake]
description = Example loss function
location = quake.py
argument-types = [ building, hazard: nullable(floating) ]
return-type = shaking_result

[type building]
type.replace = integer
type.construct = integer

[type shaking_result]
type.hazard = nullable(floating)
type.loss = floating
type.dr = floating
type.replacement = integer
```

We have defined the `building` argument and `return-value` as separate types here, for clarity.

## Older behaviour

If you have used older RiskScape releases, this next section covers some of the differences you might notice with Jython functions.

### Function parameters

In previous releases, RiskScape used to support a special `PARAMETERS` keyword for Jython functions.
These parameters were helpful if your function made certain assumptions when determining the consequence
for a given exposure and hazard - you could then vary these assumptions between model runs.
The function parameters applied across _all_ exposure and hazard input data.

The `PARAMETERS` keyword is no longer supported for Jython.
Instead, you can use :ref:`model parameters <function_parameters>` to pass assumptions to your function,
and to vary these assumptions between model runs.

### Defining argument/return types inline

An older approach used to define the function `ID`, `ARGUMENT_TYPES`, and `RETURN_TYPE` inline in the
Python code, rather than in the INI file. This is still supported in the meantime for backwards compatibility.

If you are using this approach, :ref:`jython-type-usage` has more examples of using types within a Python function.
