Implementing a new model
This example demonstrates how to implement a model in iFluid. Before implementing a model, one should understand the basics of the thermodynamic Bethe ansatz (TBA). The introductory iFluid paper contains a short review of the necessary theory.
The model
The model in question is not a real integrable model - it merely serves as a demonstration.
The implementation of a model essentially consists of programming its thermodynamic Bethe ansatz. For that we need the single particle energy and momentum along with the two-body scattering phase:
where j denotes the quasiparticle type, while A and B are the couplings of our model. Furhtermore, let's assume the quasiparticles of our model to follow fermionic statistics.
Defining the class
Create a new model by extending the iFluidCore class.
classdef myModel < iFluidCore
The constructor of our model class simply calls the constructor of the iFluidCore class.
function obj = myModel(x_grid, rapid_grid, rapid_w, couplings, Options)
obj = obj@iFluidCore(x_grid, rapid_grid, rapid_w, couplings, Ntypes, Options);
end
Implementing the TBA
Next we must implement the abstract properties and methods of the iFluidCore class.
First we specify the quasipaticle statistics:
properties (Access = protected)
quasiSpecies = 'fermion';
end
Next we must implement the energy, momentum and two-body scattering phase of our model along with their derivatives w.r.t. the rapidity:
function ebare = getBareEnergy(obj, t, x, rapid, type)
% We take B to be the second coupling
ebare = rapid.^2 + j.*obj.couplings{1,2}(t,x);
end
function pbare = getBareMomentum(obj, t, x, rapid, type)
pbare = 4*rapid;
end
function de = getEnergyRapidDeriv(obj, t, x, rapid, type)
de = 2*rapid;
end
function dp = getMomentumRapidDeriv(obj, t, x, rapid, type)
% We should return something of same length as rapid
dp = repmat(4, length(rapid), 1);
end
function dT = getScatteringRapidDeriv(obj, t, x, rapid1, rapid2, type1, type2)
% We take A to be the first coupling
dT = (type1-type2).*obj.couplings{1,1}(t,x)./( (rapid1-rapid2).^2 + obj.couplings{1,1}(t,x).^2 );
% Scattering kernel should be returned as an fluidcell
dT = fluidcell(dT);
end
Note that all operations in general should be elementwise, as x, lambda and type are most often vectors.
Finally, we have to implement functions providing the derivatives w.r.t. the couplings A and B:
function de = getEnergyCouplingDeriv(obj, coupIdx, t, x, rapid, type)
if coupIdx == 1
% Derivative w.r.t. A
de = 0;
else
% Derivative w.r.t. B
de = repmat( type, length(rapid), 1);
end
end
function dp = getMomentumCouplingDeriv(obj, coupIdx, t, x, rapid, type)
if coupIdx == 1
% Derivative w.r.t. A
dp = 0;
else
% Derivative w.r.t. B
dp = 0;
end
end
function dT = getScatteringCouplingDeriv(obj, coupIdx, t, x, rapid1, rapid2, type1, type2)
if coupIdx == 1
% Derivative w.r.t. A
dT = -(type1-type2).*(rapid1-rapid2)./( (rapid1-rapid2).^2 + obj.couplings{1,1}(t,x).^2 );
else
% Derivative w.r.t. B
dT = 0;
end
dT = fluidcell(dT);
end
And that's it! We have now implemented our own model, which is fully integrated in the iFluid framework.