To solve this optimization problem, we first need to define the decision variables and the objective function. Let's denote the number of peanut butter smoothies as \(x\) and the number of almond butter smoothies as \(y\).

The objective is to maximize profit. Given that each peanut butter smoothie generates a profit of $5 and each almond butter smoothie generates a profit of $4, the objective function can be written as:
\[ \text{Maximize: } 5x + 4y \]

We have two main constraints based on the availability of almond milk and protein powder:
1. Almond Milk Constraint: Each peanut butter smoothie requires 3 units of almond milk, and each almond butter smoothie requires 2 units. The store has a total of 50 units available.
\[ 3x + 2y \leq 50 \]

2. Protein Powder Constraint: Each peanut butter smoothie requires 1 unit of protein powder, and each almond butter smoothie requires 1.5 units. The store has a total of 40 units available.
\[ x + 1.5y \leq 40 \]

Additionally, we have non-negativity constraints since the number of smoothies cannot be negative:
\[ x \geq 0 \]
\[ y \geq 0 \]

Now, let's write this problem in Gurobi code using Python:

```python
from gurobipy import *

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

# Define the decision variables
x = m.addVar(lb=0, vtype=GRB.CONTINUOUS, name="Peanut_Butter_Smoothies")
y = m.addVar(lb=0, vtype=GRB.CONTINUOUS, name="Almond_Butter_Smoothies")

# Define the objective function
m.setObjective(5*x + 4*y, GRB.MAXIMIZE)

# Add constraints
m.addConstr(3*x + 2*y <= 50, name="Almond_Milk_Constraint")
m.addConstr(x + 1.5*y <= 40, name="Protein_Powder_Constraint")

# Optimize the model
m.optimize()

# Print the results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Peanut Butter Smoothies: {x.x}")
    print(f"Almond Butter Smoothies: {y.x}")
    print(f"Maximum Profit: {m.objVal}")
else:
    print("No optimal solution found")

```