I'm just getting started with Julia, and I'm trying to read an unformatted FORTRAN file and store the data in arrays that are shaped in a particular way. I'm not sure how to accomplish this using Julia.
I've found the Julia package FortranFiles, which provides a direct way to read unformatted FORTRAN files using Julia. The file I'm trying to read looks like:
1 integer:
[nzones]
nzones*3 integers (brackets indicate one record):
[idim1,jdim1,kdim1,idim2,jdim2,kdim2,...,
idim_nzones,jdim_nzones,kdim_nzones]
series of nzones datasets:
[xvalues1,yvalues1,zvalues1](floating point values) for 1st zone
[xvalues1,yvalues1,zvalues1](floating point values) for 2nd zone
...,
[xvalues1,yvalues1,zvalues1](floating point values) for last zone
where the first line represents the number of zones and the lines that follow represent a grid dimension in each i, j, and k directions. Following these first two records are the x, y, and z coordinates, which are Float64s, for each i, j, and k point in a zone, and I would like to shape the arrays as x(1:im,1:jm,1:km,m), y(1:im,1:jm,1:km,m), and z(1:im,1:jm,1:km,m) where im, jm, and km are the imax,jmax, and kmax extents listed for each zone. Here's what I have so far:
using FortranFiles
fname = "my_file"
fid = FortranFile(fname)
#fread fid nblks::Int32
#fread fid ni::(Int32,nblks) nj::(Int32,nblks) nk::(Int32,nblks)
Here's where I'm getting hung up. For each zone I have x, y, and z coordinate arrays which should all be rank 4 arrays. For the x array, I want to store all of the x coordinates where x[1,1,1,1] refers to an x coordinate value at i=1, j=1, k=1, and zone =1 and x[end, end, end, end] refers to an x coordinate value at i = imax, j=jmax, k=kmax, and for the last zone listed (i.,e. zone = nblks). Then I want to create similar arrays for the y and z coordinate values.
Something like:
for m = 1:nblks
im = ni[m]
jm = nj[m]
km = nk[m]
#fread fid x::(Float64,im,jm,km,m) y::(Float64,im,jm,km,m) z::(Float64,im,jm,km,m)
end
However, I get a FortranFilesError: attempting to read beyond record end when trying this approach.
It appears that my issue is somewhat related to how Julia reads unformatted binary data, which is different from how FORTRAN's read works on the same data.
In FORTRAN, I could do something like:
integer, dimension (:), allocatable :: idim, jdim, kdim
integer :: nblks, fid, ios
fid = 10
open(unit=fid,form='unformatted', file='my_file',status='old',iostat=ios)
if( ios /= 0 ) then
write(*,*) '*** Error reading file ***'
stop
end if
read(fid) nblks
allocate( idim(nblks), jdim(nblks), kdim(nblks) )
read(fid) ( idim(m), jdim(m), kdim(m), m = 1, nblks )
close(fid)
...
However in Julia, I need to keep track of the file pointer's position, and realize that each record is preceded and followed by a 4-byte integer. I haven't been able to find a way to read each zone's i, j, & k extents directly into three separate arrays like can be done in FORTRAN (since the record is probably parsed line by line), but an alternative in Julia is to just read the entire record into a single nblk*3 element vector, and then reshape this vector afterwards:
fid = open("my_file")
skip(fid,4)
nblks = read(fid,Int32)
skip(fid,8)
dims = Array{Int32}(undef,3*nblks)
read!(fid,dims)
ni, nj, nk = [Array{Int32}(undef,nblks) for i in 1:3]
for m in 1:nblks
ni[m] = dims[3*m-2]
nj[m] = dims[3*m-1]
nk[m] = dims[3*m]
end
Related
This piece of code is part of a larger function. I already created a list of molecular weights and I also defined a list of all the fragments in my data.
I'm trying to figure out how I can go through the list of fragments, calculate their molecular weight and check if it matches the number in the other list. If it matches, the sequence is appended into an empty list.
combs = [397.47, 2267.58, 475.63, 647.68]
fragments = ['SKEPFKTRIDKKPCDHNTEPYMSGGNY', 'KMITKARPGCMHQMGEY', 'AINV', 'QIQD', 'YAINVMQCL', 'IEEATHMTPCYELHGLRWV', 'MQCL', 'HMTPCYELHGLRWV', 'DHTAQPCRSWPMDYPLT', 'IEEATHM', 'MVGKMDMLEQYA', 'GWPDII', 'QIQDY', 'TPCYELHGLRWVQIQDYA', 'HGLRWVQIQDYAINV', 'KKKNARKW', 'TPCYELHGLRWV']
frags = []
for c in combs:
for f in fragments:
if c == SeqUtils.molecular_weight(f, 'protein', circular = True):
frags.append(f)
print(frags)
I'm guessing I don't fully know how the SeqUtils.molecular_weight command works in Python, but if there is another way that would also be great.
You are comparing floating point values for equality. That is bound to fail. You always have to account for some degree of error when dealing with floating point values. In this particular case you also have to take into account the error margin of the input values.
So do not compare floats like this
x == y
but instead like this
abs(x - y) < epsilon
where epsilon is some carefully selected arbitrary number.
I did two slight modifications to your code: I swapped the order of the f and the c loop to be able to store the calculated value of w. And I append the value of w to the list frags as well in order to better understand what is happening.
Your modified code now looks like this:
from Bio import SeqUtils
combs = [397.47, 2267.58, 475.63, 647.68]
fragments = ['SKEPFKTRIDKKPCDHNTEPYMSGGNY', 'KMITKARPGCMHQMGEY', 'AINV', 'QIQD', 'YAINVMQCL', 'IEEATHMTPCYELHGLRWV',
'MQCL', 'HMTPCYELHGLRWV', 'DHTAQPCRSWPMDYPLT', 'IEEATHM', 'MVGKMDMLEQYA', 'GWPDII', 'QIQDY',
'TPCYELHGLRWVQIQDYA', 'HGLRWVQIQDYAINV', 'KKKNARKW', 'TPCYELHGLRWV']
frags = []
threshold = 0.5
for f in fragments:
w = SeqUtils.molecular_weight(f, 'protein', circular=True)
for c in combs:
if abs(c - w) < threshold:
frags.append((f, w))
print(frags)
This prints the result
[('AINV', 397.46909999999997), ('IEEATHMTPCYELHGLRWV', 2267.5843), ('MQCL', 475.6257), ('QIQDY', 647.6766)]
As you can see, the first value for the weight differs from the reference value by about 0.0009. That's why you did not catch it with your approach.
I am trying to verify my RK4 code and have a state space model to solve the same system. I have a 14 state system with initial conditions, but the conditions change with time (each iteration). I am trying to formulate A,B,C,D matrices and use sys and lsim in order to compile the results for all of my states for the entire time span. I am trying to do it similar to this:
for t=1:1:5401
y1b=whatever
.
.
y14b = whatever
y_0 = vector of ICs
A = (will change with time)
B = (1,14) with mostly zeros and 3 ones
C = ones(14,1)
D = 0
Q = eye(14)
R = eye(1)
k = lqr(A,B,C,D)
A_bar = A - B*k
sys = ss(A_bar,B,C,D)
u = zeros(14,1)
sto(t,14) = lsim(sys,u,t,y_0)
then solve for new y1b-y14b from outside function
end
In other words I am trying to use sto(t,14) to store each iteration of lsim and end up with a matrix of all of my states for each time step from 1 to 5401. I keep getting this error message:
Error using DynamicSystem/lsim (line 85)
In time response commands, the time vector must be real, finite, and must contain
monotonically increasing and evenly spaced time samples.
and
Error using DynamicSystem/lsim (line 85)
When simulating the response to a specific input signal, the input data U must be a
matrix with as many rows as samples in the time vector T, and as many columns as
input channels.
Any helpful input is greatly appreciated. Thank you
For lsim to work, t has to contain at least 2 points.
Also, the sizes of B and C are flipped. You have 1 input and 1 output so u should be length of t in lsim by 1.
Lastly, it looks like you try to put all initials conditions at once in lsim with y_0 where you just want the part relevant to this iteration.
s = [t-1 t];
u = [0; 0];
if t==1
y0 = y_0;
else
y0 = sto(t-1,1:14);
end
y = lsim(sys, u, s, y0);
sto(t,1:14) = y(end,:);
I'm not sure I understood correctly your question but I hope it helps.
So I've got an interesting issue. I've got a code snippet below of a function that accepts two integers and returns two integers (x y coordinates). I generate 5 objects for it in a loop. On a Mac it returns two random numbers that are different from the others. On a PC it always returns the two exact numbers, even though I'm seeding it every time. Any ideas?
local randomSeed = 60
randomCoord = function(bufferX,bufferY)
-- randomCoord
-- int, int - get a buffer from the edge
-- returns two random coordinates that are within background Plane space
print( randomSeed )
math.randomseed(randomSeed + os.time())
randomSeed = randomSeed + os.time()
local x = math.random(backgroundBounds.xMin + bufferX,backgroundBounds.xMax - bufferX)
local y = math.random(backgroundBounds.yMin + bufferY,backgroundBounds.yMax - bufferY)
print('random x '..x..' random y '..y)
return x, y
end
backgroundBounds is just a table with integers (being the size of the backgroundBox).
Most of the time, in most languages out there, the random number facilities expect to be seeded only once. Seeding them again can produce unexpected results like returning the same sequence of numbers.
I'm trying to find a method to detect from orbital parameters (period, eccentricity, semi-major axis...) planets that are in resonance.
I know that if the ratio between two planets is commensurable, this means that they are in resonance, but suppose I want to know IN WHICH resonance they are, how can I do it?
For instance, I have my matrix of N planets and periods. How can I create a loop to check if and in which resonance the planets are?
Something like:
for i=1, N
P(i)/P(i-1)=m
if m (check the resonance condition) then
write (planets parameters)
end if
end for
Thanks a lot.
I make this program, I have a 2xN matrix in which the columns are the ID of planets and their period, the rows are the number of planets, for instance something like that:
1 0.44
1 0.8
1 0.9
2 0.9
2 1.2
3 2.0
3 3.0
The trick to change from one system of planet to the other is to rename all the planets of a system with the same number and the planets of other system with another number so, I can be able to change the resonance condition from one system to another one.
The program is simple:
read the file and save the columns and rows numbers,
create and save a matrix of col*row objects,
save as a vector the `name` and `period` of planets,
start the cycle:
for r=1,row <--- THIS MUST READ all the file
if (difference in name = 0.) then start the resonance find criterion
for l = 0,4 (number of planet in each system: THIS MUST BE MODIFIED !!)
for i = 1,5
for j = 1,5
if (i*period(l)-j*period(l+1) eq 0) <- RESONANCE CONDITION !!!
then write on file
end for
end for
end for
else write a separation between the first set and second set of planets !
end for
This is the IDL code I wrote:
pro resfind
file = "data.dat"
rows =File_Lines(file) ; per le righe
openr,lun,file,/Get_lun ; per le colonne
line=""
readf,lun,line
cols = n_elements(StrSplit(line, /RegEx, /extract))
openr,1,"data.dat"
data = dblarr(cols,rows)
readf,1,data
close,1
name = data(0,*)
period = data(1,*)
openw,2,"find.dat"
for r = 0, rows-2 DO BEGIN ;
if (name(r)-name(r+1) EQ 0) then begin
for l = 0,rows-2 do begin
for j = 1,4 do begin
for i = 1,4 do begin
if (abs(i*period(l)-j*period(l+1)) EQ 0.) then begin
printf,2, 'i resonance:', i , ' j resonance:',j,' planet ID:',l,' planet ID:',l+1
endif
endfor
endfor
endfor
endif else begin
printf,2, ' '
endfor
close,2
end
PROBLEMS:
I can't understand how to eliminate the multiply of resonance (2:4, 3:6 and so on);
in the second for loop (the one with the planet) the number of planets must be change every time but I don't understand how to change this.
First, every real number can be represented as a ratio of integers with any finite precision. That's in particular what we do when we express numbers with more and more digits in decimal system. So you need not only check whether orbital periods are in some integer-to-integer ratio, but also if the two integers are relatively small. And it's arbitrary decision, which are 'small'.
Second, remember that two floating point values are, in general, different if one is not a copy of the other. For example 3*(1/3) may be not equal to 1. That's a result of finite precision: 1/3 is infinitely repeating when represented in binary, so it gets truncated somewhere when stored in memory. So you should not check if the periods ratio is equal to some ratio but rather if it is close enough to some ratio. And its arbitrary to say what is 'close enough'.
So the fastest way would be to build an array of ratios of some relatively small integers, then sort it and remove duplicates (3:3 = 2:2, and you don't need multiple ones in your array). (Remember that duplicates are not those equal to each oher, but those close enough to each other.) Then, for each two planets calculate orbital periods ratio and binary search your table for the closest value. If it is close enough, you found a resonance.
I have two geotiff images (saying "A" and "B") imported in Matlab as matrices with Geotiffread. One has different values, while the second has only 0 and 255s.
What I'd like to do is replacing all the 255s with the values inside the other image (or matrix), according to their positions.
A and B differs in size, but they have the same projections.
I tried this:
A (A== 255)= B;
the output is the error:
??? In an assignment A(:) = B, the number of elements in A and B must be the same.
Else, I also tried with the logical approach:
if A== 255
A= B;
end
and nothing happens.
Is there a way to replace the values of A with values of B according to a specific value and the position in the referenced space?
As darthbith put in his comment, you need to make sure that the number of entries you want to replace is the same as the number values you are putting in.
By doing A(A==255)=B you are trying to put the entire matrix B into the subset of A that equals 255.
However, if, as you said, the projections are the same, you can simply do A(A==255) = B(A==255), under the assumption that B is larger or the same size as A.
Some sample code to provide a proof of concept.
A = randi([0,10],10,10);
B = randi([0,4],15,15);
C = A % copy original A matrix for comparison later
A(A==5) = B(A==5); % replace values
C==A % compare original and new
This example code creates two matrices, A is a 10x10 and B is a 15x15 and replaces all values that equal 5 in A with the corresponding values in B. This is shown to be true by doing C==A which shows where the new matrix and the old matrix vary, proving replacement did happen.
It seems to me that you are trying to mask an image with a binary mask. You can do this:
BW = im2bw(B,0.5);
A=A.*BW;
hope it helps
Try A(A==255) = B(A==255). The error is telling you that when you try to assign values to the elements of an array, you cannot give it any more or fewer values than you are trying to assign.
Also, regarding the if statement: if A==255 means the same as if all(A==255), as in, if any elements of A are not 255, false is returned. You can check this at the command line.
If you're really desperate, you can use a pair of nested for loops to achieve this (assuming A and B are the same size and shape):
[a,b] = size(A);
for ii = 1:a
for jj = 1:b
if A(ii,jj) == 255
A(ii,jj) = B(ii,jj);
end
end
end