```json
{
  "sym_variables": [
    ("x1", "number of regular rate tickets"),
    ("x2", "number of concession rate tickets")
  ],
  "objective_function": "2*x1 + 0.5*x2",
  "constraints": [
    "x1 + x2 <= 120",
    "x2 >= 15",
    "x1 >= 5*x2"
  ]
}
```

```python
from gurobipy import Model, GRB

# Create a new model
m = Model("ferry_profit")

# Create variables
regular_tickets = m.addVar(vtype=GRB.INTEGER, name="regular_tickets")
concession_tickets = m.addVar(vtype=GRB.INTEGER, name="concession_tickets")

# Set objective function
m.setObjective(2 * regular_tickets + 0.5 * concession_tickets, GRB.MAXIMIZE)

# Add constraints
m.addConstr(regular_tickets + concession_tickets <= 120, "capacity")
m.addConstr(concession_tickets >= 15, "min_concession")
m.addConstr(regular_tickets >= 5 * concession_tickets, "regular_ratio")


# Optimize model
m.optimize()

# Print results
if m.status == GRB.OPTIMAL:
    print(f"Optimal profit: ${m.objVal:.2f}")
    print(f"Regular tickets: {regular_tickets.x}")
    print(f"Concession tickets: {concession_tickets.x}")
elif m.status == GRB.INFEASIBLE:
    print("The model is infeasible.")
else:
    print(f"Optimization terminated with status {m.status}")

```
