I was wondering what a function would look like in order to change values in an array according to its location. In the script I have attached, I use a function I wrote to generate an array for voltage and current and plot these values in order to give me my IV curve. The inputdlg feature however is not in use. I would like to use the inputdlg box to re run the function for calculating I and V, then I would replace values in the first array with these new values and plot the changes. What I want to know is how to perform some function upon clicking OK on the dialog box and replacing values in an existing array with the output values from the function at a location specified by the user.
Here is where I start: all of these inputs are from edit boxes in the GUI
function plot_button_Callback(hObject, eventdata, handles)
% hObject handle to plot_button
% Get user input from GUI
NumS = str2double(get(handles.NumSeries_input,'String'));
%number of cells in series
NumP = str2double(get(handles.NumParallel_input,'String'));
%number of cells in parallel
Rs = eval(get(handles.Rseries_input,'String'));%Series resistance (Ohm)
Rsh = eval(get(handles.Rshunt_input,'String')); %Shunt resistance (Ohm)
Isc = eval(get(handles.Isc_input,'String')); %Short circuit current (A)
Voc = eval(get(handles.Voc_input,'String')); %Open circuit voltage (V)
n = eval(get(handles.n_input,'String')); %Ideality factor
% Calculate data
Here I open up a dialog box in order to set the params for the new cell
headers ={'Param1' 'Param2' 'Param3' 'Param4' 'Param5' 'Location'};
dlg_title = 'Input';
num_lines = 1;
def = round(rand(1,6)*20);
def = def';
def = num2str(def);
deff = cellstr(def);
answer = inputdlg(headers, dlg_title, num_lines, deff);
%Constants
%Parameters
CalculationIVRsRsh is defined else where in my code
T=-140; %Temperature in degree C
%Constantes
q=1.602176565e-19; %Charge elementaire (C)
k=1.3806488e-23; %Boltzmann constant (J/K)
%Size of the cell
[garb, taille]=size(Isc);
%Calculation of each cell characteristics
for ind=1:taille
[I(:,ind), V(:,ind)]=CalculationIVRsRsh(Isc(ind),Voc(ind),T,n(ind),Rs(ind),Rsh(ind));
end
Here I replicate the Current (I) array according to how many cells are in parallel
%Combination of the 3 solar cells
Isc3=min(Isc);
IscTm=repmat(Isc',1,NumP); %replicates the input vector for the Isc
%according to how many of the cells in the array are in parallel
IscT=sum(IscTm,2); %sums the Isc for the array
Isc4=min(IscT); %takes the current limitting value
Iint=-Isc3/20:0.01:min(Isc3);
IintP1=repmat(Iint,NumP,1); %replicates the values for Iint for the IV curve
IintP=sum(IintP1,1); %adds the IintP1 values to simulate the adding currents
for ind=1:taille
Vint(:,ind)=interp1(I(:,ind),V(:,ind),Iint);
IscN(ind)=interp1(V(:,ind),I(:,ind),0);
end
%We are doing the sum of V starting from the lowest I, therefore the 2
%other cells are already at a positive voltage for which the sum is the
%minimum value we can obtain for the 3 junction cell
Here I replicate the voltage array according to how many cells are in series
Isc3=min(IscN);
V3=sum(Vint,2);
Vs=repmat(V3,1,NumS);
V4=sum(Vs,2);
Voc3=sum(Voc);
VocT=NumS.*Voc3;
Then I plot new data
%plots data
V3=cat(1,V3,0);
I3=cat(1,Iint',Isc3);
I4=cat(1,IintP',Isc4);
V4=cat(1,V4,0);
plot(V3,I3,V4,I4)
axis([0 VocT+VocT/20 0 max(IscT)+max(IscT)/20]);
xlabel('V (V)');
ylabel('I (A)');
legend('Cell', 'Array');
Related
I am working with a biological model of the distribution of microbial biomass (b1) on a 2D grid. From the biomass a protein (p1) is produced. The biomass diffuses over the grid, while the protein does not. Only if a certain amount of protein is produced (p > p_lim), the biomass is supposed to diffuse.
I try to implement this by using a dummy cell variable z multiplied with the diffusion coefficient and setting it from 0 to 1 only in cells where p > p_lim.
The condition works fine and when the critical amount of p is reached in a cell, z is set to 1, and diffusion happens. However, the diffusion still does not work with the rate I would like, because to calculate diffusion, the face variable, not the value of the cell itself is used. The faces of z are always a mean of the cell with z=1 and its neighboring cells with z=0. I I, however, would like the diffusion to work at its original rate even if the neighbouring cell is still at p < p_lim.
So, my question is: Can i somehow access a faceVariable and change it? For example, set a face to 1 if any neigboring cell has reached p1 > p_lim? I guess this is not a proper mathematical thing to do, but I couldn't think of another way to simulate this problem.
I will show a very reduced form of my model below. In any case, I thank you very much for your time!
##### produce mesh
nx= 5.
ny= nx
dx = 1.
dy = dx
L = nx*dx
mesh = Grid2D(nx=nx,ny=ny,dx=dx,dy=dy)
#parameters
h1 = 0.5 # production rate of p
Db = 10. # diffusion coeff of b
p_lim=0.1
# cell variables
z = CellVariable(name="z",mesh=mesh,value=0.)
b1 = CellVariable(name="b1",mesh=mesh,hasOld=True,value=0.)
p1= CellVariable(name="p1",mesh=mesh,hasOld=True,value=0.)
# equations
eqb1 = (TransientTerm(var=b1)== DiffusionTerm(var=b1,coeff=Db*z.arithmeticFaceValue)-ImplicitSourceTerm(var=b1,coeff=h1))
eqp1 = (TransientTerm(var=p1)==ImplicitSourceTerm(var=b1,coeff=h1))
# set b1 to 10. in the center of the grid
b1.setValue(10.,where=((x>2.)&(x<3.)&(y>2.)&(y<3.)))
vi=Viewer(vars=(b1,p1),FIPY_VIEWER="matplotlib")
eq = eqb1 & eqp1
from builtins import range
for t in range(10):
b1.updateOld()
p1.updateOld()
z.setValue(z + 0.1,where=((p1>=p_lim) & (z < 1.)))
eq.solve(dt=0.1)
vi.plot()
In addition to .arithmeticFaceValue, FiPy provides other interpolators between cell and face values, such as .harmonicFaceValue and .minmodFaceValue.
These properties are implemented using subclasses of _CellToFaceVariable, specifically _ArithmeticCellToFaceVariable, _HarmonicCellToFaceVariable, and _MinmodCellToFaceVariable.
You can also make a custom interpolator by subclassing _CellToFaceVariable. Two such examples are _LevelSetDiffusionVariable and ScharfetterGummelFaceVariable (neither is well documented, I'm afraid).
You need to override the _calc_() method to provide your custom calculation. This method takes three arguments:
alpha: an array of the ratio (0-1) of the distance from the face to the cell on one side, relative to the distance from distance from the cell on the other side to the cell on the first side
id1: an array of indices of the cells on one side of the face
id2: an array of indices of the cells on the other side of the face
Note: You can ignore any clause if inline.doInline: and look at the _calc_() method defined under the else: clause.
I have 3 graphs of an IV curve (monotonic increasing function. consider a positive quadratic function in the 1st quadrant. Photo attached.) at 3 different temperatures that are not obtained linearly. That is, one is obtained at 25C, one at 125C and one at 150C.
What I want to make is an interpolated 2D array to fill in the other temperatures. My current method to build a meshgrid-type array is as follows:
H = 5;
W = 6;
[Wmat,Hmat] = meshgrid(1:W,1:H);
X = [1:W; 1:W];
Y = [ones(1,W); H*ones(1,W)];
Z = [vecsatIE25; vecsatIE125];
img = griddata(X,Y,Z,Wmat,Hmat,'linear')
This works to build a 6x6 array, which I can then index one row from, then interpolate from that 1D array.
This is really not what I want to do.
For example, the rows are # temps = 25C, 50C, 75C, 100C, 125C and 150C. So I must select a temperature of, say, 50C when my temperature is actually 57.5C. Then I can interpolate my I to get my V output. So again for example, my I is 113.2A, and I can actually interpolate a value and get a V for 113.2A.
When I take the attached photo and digitize the plot information, I get an array of points. So my goal is to input any Temperature and any current to get a voltage by interpolation. The type of interpolation is not as important, so long as it produces reasonable values - I do not want nearest neighbor interpolation, linear or something similar is preferred. If it is an option, I will try different kinds of interpolation later (cubic, linear).
I am not sure how I can accomplish this, ideally. The meshgrid array does not need to exist. I simply need the 1 value.
Thank you.
If I understand the question properly, I think what you're looking for is interp2:
Vq = interp2(X,Y,V,Xq,Yq) where Vq is the V you want, Xq and Yq are the temperature and current, and X, Y, and V are the input arrays for temperature, current, and voltage.
As an option, you can change method between 'linear', 'nearest', 'cubic', 'makima', and 'spline'
I am trying to perform a calculation using two different matrices, but they have come in slightly different forms.
The one matrix (for interests sake) are filled with reflectance values of a material from wavelengths 200nm to 2600nm, so each individual wavelength, in increments of 1 has a reflectance value.
The second matrix is a solar energy matrix which stores the amount of energy that is present at each wavelength. This one however has irregular steps and ranges from 280nm to 4000nm. But from 280nm-400nm it is in steps of 0.5nm, from 400nm-1705nm it is in steps of 1nm, and from 1750nm-4000nm it is steps of 5nm.
What I have been trying to do, unsucessfully thus far, is to edit this solar energy matrix so that it gives the entire range in steps of 1nm.
filename='H:\I_sol data.csv';
Dataisol = csvread(filename,1,0);
for j=1:1:count
if Dataisol(j,:)~=Dataisol(j+1,:)-1 %compare the wavelength to the value of the next wavelegth
newx=(Dataisol(j,:)+[1,0]) %if the next wavelength is not 1 larger than the previous, add a new row
newx(1,2)=NaN %make the new row to add blank
Dataisol=insertrows(Dataisol, newrow, j+1) %insert the new blank row
end
end
Above is what I have started with, at the moment I am just trying to fill the gaps by adding in new rows where there is a 5nm jump between wavelengths. Once i am able to create the missing elements, then I will turn my attention to populating them with the correct values (probably the midpoint between the 2 given values)
My end goal is going to be to trim both of the matrices so that they both have the same starting and ending wavelength and both have increments of 1nm throughout (also for interest sake, or for advice if this is trivial for someone). If anyone knows how to fill these gaps or make the necessary changes to the matrix it would be a great help!
Example of the csv file:
Wvlgth nm Etr W*m-2*nm-1
280.0 8.2000E-02
280.5 9.9000E-02
281.0 1.5000E-01
281.5 2.1200E-01
282.0 2.6700E-01
282.5 3.0300E-01
283.0 3.2500E-01
283.5 3.2300E-01
284.0 2.9900E-01
284.5 2.5024E-01
285.0 1.7589E-01
285.5 1.5500E-01
286.0 2.4200E-01
... .....
428.0 1.6510E+00
429.0 1.5230E+00
430.0 1.2120E+00
431.0 1.0990E+00
432.0 1.8220E+00
433.0 1.6913E+00
434.0 1.5600E+00
435.0 1.7090E+00
436.0 1.8680E+00
437.0 1.9000E+00
438.0 1.6630E+00
439.0 1.6010E+00
440.0 1.8300E+00
.... .....
2205.0 8.0900E-02
2210.0 8.0810E-02
2215.0 8.0410E-02
2220.0 7.9990E-02
2225.0 7.8840E-02
2230.0 7.8400E-02
2235.0 7.7930E-02
2240.0 7.6510E-02
2245.0 7.6250E-02
2250.0 7.5370E-02
... .....
Here is the code I use for assigning the variables to be used in the interp1 function, which is called as follows:
solx=Dataisol(:,1);
soly=Dataisol(:,2);
xi=280:1:2600;
newsol = [xi interp1(solx,soly,xi,'linear','extrap')];
The values that are stored in these variables as well as the error I am receiving are given below:
The function you need here is interp1. Set xi to be a vector of all the wavelengths you want to consider, say xi=280:1:2600;.
if wavelength is a vector of all your irregular values from the file, and sol is the corresponding vector of all the solar energies (you can use column references for your single matrix here as well)
newsol = [xi interp1(wavelength,sol,xi,'linear','extrap')];
will give you a new matrix with wavelengths increasing by 1 in column 1, and column 2 will contain values directly from your file where they exist and linearly interpolated values where they do not.
I'm using Matlab to create a GUI. Herefore I'm using the guide function of matlab.
I want to store the slider values in a vector. I am doing this in the callback function:
for i = 1:10
X(i) = get(handles.slider1,'Value');
end
But this results in a vector that stores the same value 10 times. What I actually want is to store the last 10 values of the slider in a vector. Any ideas?
I would suggest to create a 1 x 10 vector of zeros when starting the GUI, i.e. in the OpeningFcn of the GUI:
handles.X = zeros(1,10);
guidata(hObject,handles); % Update handles variable
Then in the Callback function of the slider, you always shift the vector one to the right and add the new value in the first place:
x = get(handles.slider1,'Value');
handles.X = [x, handles.X(1:end-1)];
guidata(hObject,handles); % Update handles variable
Now X always contains the last 10 values of the slider value, X(1) is the last value and so on.
Before the slider hasn't been moved 10 times, some of the values will not be correct, i.e. they will just be zero. If this is a problem, you could grow the X vector dynamically in the Callback.
I have a cell array it is 100x1, variable name is CellA. As you will see from the png file attached, the cell array contains various size matrices in each of the 100 different cells.
I want to extract that data. Actually, I am trying to find the number of unique elements in each cell and their frequency. Below is my code which gets dimensional errors:
for i=1:length(CellA)
if isempty(CellA{i})
continue;% do nothing
else
unqmz(i,:) = unique(CellA{i})';
countmz(i,:) = histc(CellA{i}, unqmz(i))';
end
Eventually, I want to plot count versus unique number for each different cell array where the total number of counts for that cell exceeds a pre-determined value. e.g.) 4
You can also do it this way:
unqmz = cellfun(#unique, CellA, 'uni', 0);
countmz = arrayfun(#(n) histc(CellA{n},unqmz{n}), 1:numel(CellA), 'uni', 0).';
See if this works out for you -
%// Get result data into unqmz and countmz
unqmz = cell(numel(CellA),1);
countmz = cell(numel(CellA),1);
for ii=1:numel(CellA)
if isempty(CellA{ii})
continue;% do nothing
else
unqmz(ii) = {unique(CellA{ii})'} %//'
countmz(ii) = {histc(CellA{ii}, cell2mat(unqmz(ii)))'} %//'
end
end
%// Count of number of unique numbers in each cell
count_uniqs = cellfun(#numel,unqmz);
%// Plot (assuming you want to plot everything in one figure window)
figure,
for k = 1:size(countmz,1)
if count_uniqs(k)>4
plot(cell2mat(unqmz(k)),cell2mat(countmz(k)),'x')
hold on
end
end