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.