Yet another "Dot indexing is not supported for variables of this type"

5 views (last 30 days)
Hello,
I am writing a programm that tries to simulate reflection and transmission of ultrasonic waves in between small layers of different materials. I am unsure where the issue really is and the threats for this type of error message couldn't really help me. I attached the original files but I will run you through in this post again:
I have defined a class like the following (Sorry the variables are partially labled in German)
classdef U_Interface < handle
%At each interface there is transmission and reflection of a wave,
% depending on the direction it is coming from
properties
time_max; %maximum of time to look at
N_Interfaces; %number of Interfaces
c_eins; %sound velocity before
c_zwei; %after
rho_eins; %density before
rho_zwei; %after
GF_below; %Interface above
GF_above; %below
d_eins; %distance before
d_zwei %after
r_1; %reflection coefficients
r_2;
t_1; %transmission coefficients
t_2;
z_pos; %position
wave_time; %time vector of the wave
wave_amplitude; %amplitude vector
end
Now I have a function which creates such objects like this:
methods
function obj = U_Interface(pos,c1,c2,d1,d2,rho1,rho2,t_max,NI)
obj.time_max=t_max;
obj.N_Interfaces=NI;
obj.z_pos=pos;
obj.c_eins=c1;
obj.c_zwei=c2;
obj.rho_eins=rho1;
obj.rho_zwei=rho2;
obj.d_eins=d1;
obj.d_zwei=d2;
obj.r_1=(c2*rho2-c1*rho1)/(c2*rho2+c1*rho1);
obj.r_2=-(c2*rho2-c1*rho1)/(c2*rho2+c1*rho1); %=-r_1
obj.t_1=(2*c2*rho2)/(c2*rho2+c1*rho1); %t=1+r
obj.t_2=(2*c1*rho1)/(c2*rho2+c1*rho1);
obj.wave_time=[];
obj.wave_amplitude=[];
end
%...
end
and one so that the program knows which layers are above and below which other ones (also in methods):
function [] = pair(obj,a,b)
obj.GF_below = a;
obj.GF_above = b;
end
I wrote a function which looks like this to read in my values that I will use.
methods (Static) %doesn't use obj
function counter = searchtext(filename, str)
%looks for str in file
%read file with infos
ID=fopen(filename, 'rt');
if ID == -1
error('Cannot open file: %s', filename);
end
tline = fgetl(ID); %first line
if tline == -1
error('File is empty')
end
counter=1; %current line
while ~feof(ID) %until end of file
if contains(tline, str)
break
end
tline=fgetl(ID); %next line
counter=counter+1;
end
fclose(ID);
end
end
The file is a txt and just a tabular with the material at the beginning and then speed of sound and density for that material. I have it attached to this post (although it is in German, not as in this thread) if you find that useful. I am then creating my objects with that function like this:
n=3; %number of layers
t_max=5e-6; %5 micro seconds maximum
a=.5; %amplitude
c_1=6000; %just anything
d_1=0;
rho_1=5; %just anything
%1: nothing into water
%2: water zu steel
%3: steel to a very dense medium
GF{n+1}=0;
for i=1:n
%Input für every layer (f.e.: steel, 5e-4, water, 1e-3
if i<n
prompt=sprintf('please type in the material of layer #%d: ', i);
material=input(prompt,'s');
d_2=input('distance: ');
else
Material='nothing';
d_2=0;
end
%function that looks for line with material
count=U_Interface.searchtext('GF-tabular.txt',material);
%if it doesn't find it, it uses 'nothing'
fID=fopen('GF-tabular.txt');
textscan(fID,'%*[^\n]',count-1); %skip lines until there
values=textscan(fID,'%s %f %f',1);
c_2=values{2}; %[m/s]
rho_2=values{3};%[kg*m^-3]
fclose(fID);
%create Interfaces with corresponding values
%U_Interface depends on pos,c(2x),d(2x),rho(2x),t_max,NI
GF{i}=U_Interface(i,c_1,c_2,d_1,d_2,rho_1,rho_2,t_max,n);
if i==1
pair(GF{i},0,GF{i+1}); %(below, above)
else
pair(GF{i},GF{i-1},GF{i+1}); %#i zwischen #i-1 und #i+1
end
c_1=c_2;
d_1=d_2; %layers are progressive i.e. d1 water d2 -> d1 steel d2 -> d1 something d2
rho_1=rho_2;
end
Now I start simulating it with the following two functions (also from methods):
function [] = wave_from_down(obj,time,amplitude)
%"time" keeps track of time :)
%for every iteration add corresponding time and amplitude
obj.wave_time=[obj.wave_time;time];
obj.wave_amplitude=[obj.wave_amplitude;amplitude];
%reflection
if obj.z_pos>1 %there is no layer below GF{1}
t_r=time+obj.d_eins/obj.c_eins; %t+(time-it-needs-to-pass)
if t_r<obj.time_max %it has to stop eventually
obj.GF_below.wave_from_up(t_r,amplitude*-obj.r_1); %r_2
%y passed on to layer below
end
end
%transmission
if obj.z_pos<obj.N_Interfaces %until last layer
t_r=time+obj.d_zwei/obj.c_zwei;
if t_r<obj.time_max
obj.GF_above.wave_from_down(t_r,amplitude*obj.t_2)
%passed on to layer above
end
end
end
function [] = wave_from_up(obj,time,amplitude)
obj.wave_time=[obj.wave_time;time];
obj.wave_amplitude=[obj.wave_amplitude;amplitude];
%reflection
if obj.z_pos<obj.N_Interfaces
t_r=time+obj.d_eins/obj.c_eins;
if t_r<obj.time_max
obj.GF_above.wave_from_down(t_r,amplitude*obj.r_1)
%passed on to layer above
end
end
%transmission
if obj.z_pos>1
t_r=time+obj.d_zwei/obj.c_zwei;
if t_r<obj.time_max
obj.GF_below.wave_from_up(t_r,amplitude*obj.t_1)
%passed on to layer below
end
end
end
I want to use this simulation to be able to plot the amplitude (after considering interference etc.) over time later. While debugging I found that the objects GF are produced correctly and also for the first couple of iterations updating wave_time and wave_amplitude through the functions seems to work fine. However, it somehow seems to change the values mid-through so that GF_above becomes an empty array while GF_below is simply 0. I really don't know why it happens. It then says as an error message:
Dot indexing is not supported for variables of this type.
Error in U_Interface/wave_from_up (line 106)
obj.GF_above.wave_from_down(t_r,amplitude*obj.r_1)
Error in U_Interface/wave_from_down (line 79)
obj.GF_below.wave_from_up(t_r,amplitude*-obj.r_1); %r_2
Error in U_Interface/wave_from_up (line 106)
obj.GF_above.wave_from_down(t_r,amplitude*obj.r_1)
Error in U_Interface/wave_from_down (line 79)
obj.GF_below.wave_from_up(t_r,amplitude*-obj.r_1); %r_2
Error in U_Interface/wave_from_up (line 106)
obj.GF_above.wave_from_down(t_r,amplitude*obj.r_1)
Error in U_Interface/wave_from_down (line 79)
obj.GF_below.wave_from_up(t_r,amplitude*-obj.r_1); %r_2
Error in U_Interface/wave_from_down (line 88)
obj.GF_above.wave_from_down(t_r,amplitude*obj.t_2)
Error in Simul (line 50)
wave_from_down(GF{1},0,a);
And when you then type in afterwards
GF{1}
it displays the correct attributes again (GF_above is an interface and GF_below is 0)
If you try and run it like this it actually works just fine however so the mistake probably is within the file reading section
GF{1}=U_Interface(1,6000,1500,0,1e-3,5,1,5e-6,3);
GF{2}=U_Interface(2,1500,4300,1e-3,5e-4,1,4,5e-6,3);
GF{3}=U_Interface(3,4300,1e10,5e-4,0,4,1e10,5e-6,3);
GF{1}.pair(0,GF{2});
GF{2}.pair(GF{1},GF{3});
GF{3}.pair(GF{2},0);
GF{1}.wave_from_up(0,a); %a=.5
I am running MATLAB student version: 9.6.0.1174912 (R2019a), Update 5. Also I am new to MATLAB so my mistake might be very simple even though the code looks complex.
I would be very glad if someone could take some time and help me with this since it is part of a bigger project I am working on. So I am sorry this post is so long, but I am unsure where the issue really is.
Thank you for reading and have a great day!
  1 Comment
Erik Weigert
Erik Weigert on 1 Nov 2019
Thank you guys for all of your answers! I really didn't expect any answers to come that fast, but I guess I may post some more questions then in the future :) What a great community this must be. You made some great remarks about how I can improve the code both to make it less buggy and to improve the structure. In the end the greatest mistake however was to create and pair the Interfaces both within one loop. It paired GF{i} with GF{i+1} which of course didn't exist yet! However, that's why it just created the interface by itself and that's where all the weird stuff began. I resolved this issue and could improve my code by a lot thanks to you ^^ Thanks a lot again and have a great weekend

Sign in to comment.

Accepted Answer

Daniel M
Daniel M on 28 Oct 2019
Edited: Daniel M on 29 Oct 2019
First of all, bravo for posting a very comprehensive question that is self-contained. Most people don't do this and it makes it difficult to help. (The only thing I had to change to get it to run was to hardcode the inputs Material = 'Nix' and d_2 = 0).
You need to be very careful when working with objects that inherit from the handle class. Your script behaves differently upon successive runs. The first time, it errors in wave_from_down, when it calls obj.GF_above.wave_from_down(t_r,amplitude*obj.t_2). That is because you have initialized GF{1}.GF_above to be empty. This happened in the first call to pair(), which I think is another source of problems.
The second time you run the script (without using clear() between runs), it goes
% Line 57 in Simul: wave_from_down(GF{1},0,a);
% Line 88 in U_Interface: obj.GF_above.wave_from_down(t_r,amplitude*obj.t_2)
% Line 79 in U_Interface: obj.GF_below.wave_from_up(t_r,amplitude*-obj.r_1); %r_2
% Line 106 in U_Interface: obj.GF_above.wave_from_down(t_r,amplitude*obj.r_1)
upon which it fails, because at that point obj.GF_above is empty.
At this point, it was already too complicated to trace what is happening to the objects. So, I added a property to the class called 'Name', and in Simul, set it to help keep track.
GF{i}.Name = i; % after initializing object with U_Interface
So, now try this:
clearvars
Simul % run your script
% once it errors, type:
GF{1}
%{
% ans =
U_Interface with properties:
Name: 1
time_max: 5e-06
N_Interfaces: 3
c_eins: 6000
c_zwei: 1e+10
rho_eins: 5
rho_zwei: 1e+10
GF_below: 0
GF_above: []
d_eins: 0
d_zwei: 0
r_1: 1
r_2: -1
t_1: 2
t_2: 6e-16
z_pos: 1
wave_time: 0
wave_amplitude: 0.5
%}
Simul % run it again, without clearvars
% when it errors:
GF{1}
%{
ans =
U_Interface with properties:
Name: 1
time_max: 5e-06
N_Interfaces: 3
c_eins: 6000
c_zwei: 1e+10
rho_eins: 5
rho_zwei: 1e+10
GF_below: 0
GF_above: [1×1 U_Interface]
d_eins: 0
d_zwei: 0
r_1: 1
r_2: -1
t_1: 2
t_2: 6e-16
z_pos: 1
wave_time: 0
wave_amplitude: 0.5
%}
GF{1}.GF_above
%{
ans =
U_Interface with properties:
Name: 2
time_max: 5e-06
N_Interfaces: 3
c_eins: 1e+10
c_zwei: 1e+10
rho_eins: 1e+10
rho_zwei: 1e+10
GF_below: [1×1 U_Interface]
GF_above: []
d_eins: 0
d_zwei: 0
r_1: 0
r_2: 0
t_1: 1
t_2: 1
z_pos: 2
wave_time: 0
wave_amplitude: 3e-16
%}
GF{1}.GF_above.GF_below
%{
ans =
U_Interface with properties:
Name: 1
time_max: 5e-06
N_Interfaces: 3
c_eins: 6000
c_zwei: 1e+10
rho_eins: 5
rho_zwei: 1e+10
GF_below: 0
GF_above: []
d_eins: 0
d_zwei: 0
r_1: 1
r_2: -1
t_1: 2
t_2: 6e-16
z_pos: 1
wave_time: [2×1 double]
wave_amplitude: [2×1 double]
%}
Aha! GF{1}.GF_above.GF_below is also Named 1, so it should be the same as GF{1} right?
isequal(GF{1},GF{1}.GF_above.GF_below)
% ans = 0
But if you do this:
clearvars
Simul
% let it error
isequal(GF{1},GF{2}.GF_below)
% ans = 1
GF{1}.GF_above = GF{2};
isequal(GF{1},GF{1}.GF_above.GF_below)
% ans = 1
So why didn't it work before!?
Clearly, things are not being handled properly.
I recommend using clearvars at the beginning of your script, to avoid headaches like this.
Also I recommend reading up on how to work with arrays of Handle Objects
  2 Comments
Daniel M
Daniel M on 28 Oct 2019
Edited: Daniel M on 29 Oct 2019
Instead of trying to link together the different objects, storing them within the properties, you could try linking together the Names of the objects.
For example,
function obj = setLinks(obj)
objInd = obj.Name;
wrap3 = @(x) (1+mod(x-1,3)); % used to circularly reference the vector [1 2 3]
% general case of wrap3 is:
% wrapN = @(x,N) (1+mod(x-1,N)); % where N is length of object array
obj.GF_below = wrap3(objInd-1);
obj.GF_above = wrap3(objInd+1);
end
Then, when you work with your objects, pass the entire object array to the function, and an index indicating "work on this object". And when you try to access the object above or below, you can do this:
function objAbove = getObjAbove(objArray,objInd)
thisObject = objArray(objInd); % or objArray{objInd} depending on the type
objAbove = objArray(thisObject.GF_above); % or use {}
end
I think this would make this a lot more clear.
Daniel M
Daniel M on 28 Oct 2019
And then, of course what Steven said too. If you are setting boundaries, e.g. GF{1}.below = 0, then this is not a U_interface object. Your functions need to be able to handle the cases where GF{i}.above/below is empty, or is a boundary, etc (general error checking).

Sign in to comment.

More Answers (1)

Steven Lord
Steven Lord on 28 Oct 2019
Set an error breakpoint so MATLAB stops when the code would throw an error. Attempt to reproduce the error. When MATLAB reaches the debug prompt (K>>) on line 106 of your wave_from_up function, check whether the various attempts to index into the obj variable and its properties should all "make sense".
Does obj have properties named GF_above and r_1?
Does obj.GF_above have a field, property, or function/method named wave_from_down?
Does obj.GF_below have a field, property, or function/method named wave_from_down?
Based on this statement you wrote:
it displays the correct attributes again (GF_above is an interface and GF_below is 0)
0 doesn't have a field, property, or function/method named wave_from_down.
It seems to me like you're missing some sort of "base case" handling in your recursive layer processing functions. You may want to restrict what classes the GF_above and GF_below properties of the U_Interface class can contain, and write such a "base case" layer to handle the "there's nothing past here, this is the top/bottom of the stack" case.

Categories

Find more on Graphics Object Programming in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!