Related
I am trying to save multiple solutions of my ODE in an Array. Right now this is what I got:
sols = []
for i in 1:numSim
if solver == "Rosenbrock23"
solution = solve(odeprob, Rosenbrock23())
append!(sols, solution)
end
end
As you can see I only want to append to this Array, if a certain ode solver is used. However, the "append!" statement neglects this statement and runs every iteration of the loop. I tried preallocating the array sol, to use a statement like this:sols[i] = solution
But here im struggling with the type declaration of the array sol.
I tried
sols = zeros(length)
and then
sols[i] = solution
However solution is of type ODESolution and can not be converted to Float64
Please provide adequate information to get an exact answer. I can't reproduce your problem since you didn't mention what is odeprob or the value of numSim. Since you declared sols = [] then the eltype of sols should be Any; hence it should be capable to contain any element.
However, I can replicate the code in the official doc (Example 1 : Solving Scalar Equations (ODE)), and combine it with your approach:
using DifferentialEquations
f(u,p,t) = 1.01*u
u0 = 1/2
tspan = (0.0,1.0)
odeprob = ODEProblem(f,u0,tspan)
sols = []
numSim = 2
solver = "Rosenbrock23"
for i in 1:numSim
if solver == "Rosenbrock23"
solution = solve(odeprob, Rosenbrock23())
append!(sols, solution)
end
end
Then if I call the sols variable:
julia> sols
22-element Vector{Any}:
0.5
0.5015852274675505
0.5177172276935554
0.5423763076371255
0.5852018498590001
0.6425679823795596
0.7275742030693312
0.835930076418226
0.9846257728490266
1.1713401410831334
1.3738764155543854
0.5
0.5015852274675505
0.5177172276935554
0.5423763076371255
0.5852018498590001
0.6425679823795596
0.7275742030693312
0.835930076418226
0.9846257728490266
1.1713401410831334
1.3738764155543854
I want to assign complex array as variable.
My code is like
complex indx(3,3)
integer i,j
do i=1,3
do j=1,3
indx(i,j) = (i,j)
write(*,*) indx(i,j)
end do
end do
and in this case I am getting an error like
A symbol must be a defined parameter in this context. [I]
indx(i,j) = (i,j)
You must use function cmplx to build a complex value you want to assign.
complex indx(3,3)
integer i,j
do i=1,3
do j=1,3
indx(i,j) = cmplx(i,j)
write(*,*) indx(i,j)
end do
end do
The syntax you tried is only valid for constant literals.
The answer by Vladimir F tells the important part: for (i,j) to be a complex literal constant i and j must be constants.1 As stated there, the intrinsic complex function cmplx can be used in more general cases.
For the sake of some variety and providing options, I'll look at other aspects of complex arrays. In the examples which follow I'll ignore the output statement and assume the declarations given.
We have, then, Vladimir F's correction:
do i=1,3
do j=1,3
indx(i,j) = CMPLX(i,j) ! Note that this isn't in array element order
end do
end do
We could note, though, that cmplx is an elemental function:
do i=1,3
indx(i,:) = CMPLX(i,[(j,j=1,3)])
end do
On top of that, we can consider
indx = RESHAPE(CMPLX([((i,i=1,3),j=1,3)],[((j,i=1,3),j=1,3)]),[3,3])
where this time the right-hand side is in array element order for indx.
Well, I certainly won't say that this last (or perhaps even the second) is better than the original loop, but it's an option. In some cases it could be more elegant.
But we've yet other options. If one has compiler support for complex part designators we have an alternative for the first form:
do i=1,3
do j=1,3
indx(i,j)%re = i
indx(i,j)%im = j
end do
end do
This doesn't really give us anything, but note that we can have the complex part of an array:
do i=1,3
indx(i,:)%re = [(i,j=1,3)]
indx(i,:)%im = [(j,j=1,3)]
end do
or
do i=1,3
indx(i,:)%re = i ! Using scalar to array assignment
indx(i,:)%im = [(j,j=1,3)]
end do
And we could go all the way to
indx%re = RESHAPE([((i,i=1,3),j=1,3))],[3,3])
indx%im = RESHAPE([((j,i=1,3),j=1,3))],[3,3])
Again, that's all in the name of variety or for other applications. There's even spread to consider in some of these. But don't hate the person reviewing your code.
1 That's constants not constant expresssions.
I am fairly new to Fortran programming so this might be an obvious issue, so bear with me.
Here is the code I am working with:
program A1H
! Householder transformation
implicit none
integer,parameter::dp=selected_real_kind(15,307) ! Double precision kind
real(kind=dp), dimension(6,3)::A
real(kind=dp), dimension(6,1)::b
integer, dimension(6,6)::Pglobal ! Global identity matrix
integer::i,j,g
g = size(A,1)
do j=1,g
do i=1,g
Pglobal(i,j) = (i/j)*(j/i)
end do
end do
b(:,1) = [1237,1941,2417,711,1177,475]
A(1,:) = [1,0,0]
A(2,:) = [0,1,0]
A(3,:) = [0,0,1]
A(4,:) = [-1,1,0]
A(5,:) = [-1,0,1]
A(6,:) = [0,-1,1]
call mat_print('A',A)
call mat_print('b',b)
call mat_print('Pglobal',Pglobal)
call householder(A,b)
contains
subroutine householder(A,b)
real(kind=dp), intent(in)::A(:,:),b(:,:)
real(kind=dp)::alpha,gamma,beta
real(kind=dp), dimension(6,6)::H
real(kind=dp), dimension(6,3)::y,aa
real(kind=dp), dimension(6,1)::yy,v,dglobal,ek,bb
real(kind=dp), dimension(1,6)::d
integer::k,m,n,j
m = size(A,1)
n = size(A,2)
aa = A
bb = b
do k=1,n
dglobal(:,k) = [0,0,0,0,0,0]
alpha = -sign(aa(k,k),aa(k,k))*norm2(aa(k:m,k))
ek(:,1) = Pglobal(:,k)
dglobal(k:m,k) = aa(k:m,k)
v(:,k) = (dglobal(:,k)) - alpha*ek(:,1)
d(k,:) = v(:,k)
beta = dot_product(d(k,:),v(:,k))
if (beta==0) then
continue
end if
H = Pglobal - (2/beta)*(matmul(reshape(v(:,k),(/m,1/)),reshape(d(k,:),(/1,m/))))
y = matmul(H,aa)
yy = matmul(H,bb)
aa = y
bb = yy
call mat_print('aa',y)
call mat_print('bb',yy)
end do
end subroutine
! Matrix print subroutine
subroutine mat_print(b,a)
character(*), intent(in)::b
class(*), intent(in)::a(:,:)
integer::i
print*,' '
print*,b
do i=1,size(a,1)
select type (a)
type is (real(kind=dp)) ; print'(100f9.4)',a(i,:)
type is (integer) ; print'(100i9 )',a(i,:)
end select
end do
print*,' '
end subroutine
end program
The issue I'm having is that when I change the variable aa to another name, I get the wrong result for the y variable; if I leave it as is, it is correct; however, leaving the bb variable as is, the yy result is incorrect, but if I change the bb variable to another other name, I get the correct result for yy, but then my answer for y changes! I'm very confused how this can happen as my experience from coding tells me that the answer should not change based on a simple variable name change, and if you look at the code, the y and yy variables are not even connected in the algorithm. This is not the only Fortran code I have run into this issue before on. Any help would be much appreciated.
I was able to reproduce your bug with GNU Fortran (Homebrew GCC 8.2.0) 8.2.0. There is indeed a bug in your program. You can find this bug by compiling with the option -fbounds_check. When you run it, you will find that several of your array access don't make sense. For example, you access dglobal(:,k) = [0,0,0,0,0,0], but the second dimension of dglobal is only 1. Use this flag to help fix your code, and I'm sure this bug will disappear.
For anyone who wants to dig deep into why this bug's appearance depends on the variable name, I was able to reproduce it with the array name test_array, but not with other shorter names. I was also able to get the correct answer using the test_array name if I set -fmax-stack-var-size=100, and other values appeared with different sizes. My guess is that gfortran puts these arrays on the stack, and the order is based on the name. Certain names put it in a "safe" location, so that the values are not overwritten by the buffer overflow.
I am trying to take a derivative of an array but am having trouble. The array is two dimensional, x and y directions. I would like to take a derivative along x and along y using central difference discretization. The array has random values of numbers, no values are NaN. I will provide a basic portion of the code below to illustrate my point (assume the array u is defined and has some initial values already inputted into it)
integer :: i,j
integer, parameter :: nx=10, ny=10
real, dimension(-nx:nx, -ny:ny) :: u,v,w
real, parameter :: h
do i=-nx,nx
do j=-ny,ny
v = (u(i+1,j)-u(i-1,j))/(2*h)
w = (u(i,j+1)-u(i,j-1))/(2*h)
end do
end do
Note, assume the array u is defined and filled up before I find v,w. v,w are supposed to be derivatives of the array u along x and along y,respectively. Is this the correct way to take a derivative of an array?
I can see several problems in your code.
1.You must be careful what you have on the left hand side.
v = (u(i+1,j)-u(i-1,j))/(2*h)
means that the whole array v will be set to the same number everywhere. You don't want this in a loop. In a loop you want to set just one point at a time
v(i,j) = (u(i+1,j)-u(i-1,j)) / (2*h)
and 2) You are accessing the array out of bounds. You can keep the simple loop, but you must use the boundary points as "ghost points" which store the boundary values. If I assume that points -nx,nx,-nyandny` are lying on the boundary, then you can only compute the derivative using the central difference inside the domain:
do i=-nx+1,nx-1
do j=-ny+1,ny-1
v(i,j) = (u(i+1,j)-u(i-1,j)) / (2*h)
w(i,j) = (u(i,j+1)-u(i,j-1)) / (2*h)
end do
end do
If you need the derivative on the boundary, you must use a on-sided difference like
do j=-ny+1,ny-1
v(nx,j) = (u(nx,j)-u(nx-1,j)) / h
w(nx,j) = (u(nx,j+1)-u(nx,j-1)) / h
end do
I've searched and there are many answers to this kind of question, suggesting functions like arrayfun, bsxfun, and so on. I haven't been able to resolve the issue due to dimension mismatches (and probably a fundamental misunderstanding as to how MATLAB treats anonymous function handles).
I have a generic function handle of more than one variable:
f = #(x,y) (some function of x, y)
Heuristically, I would like to define a new function handle like
g = #(x) sum(f(x,1:3))
More precisely, the following does exactly what I need, but is tedious to write out for larger arrays (say, 1:10 instead of 1:3):
g = #(x) f(x,1)+f(x,2)+f(x,3)
I tried something like
g = #(x) sum(arrayfun(#(y) f(x,y), 1:3))
but this does not work as soon as the size of x exceeds 1x1.
Thanks in advance.
Assuming you cannot change the definition of f to be more vector-friendly, you can use your last solution by specifying a non-uniform output and converting the output cell array to a matrix:
g = #(x) sum(cell2mat(arrayfun(#(y) f(x,y), 1:3,'UniformOutput',false)),2);
This should work well if f(x,y) outputs column vectors and you wish to sum them together. For rows vectors, you can use
g = #(x) sum(cell2mat(arrayfun(#(y) f(x,y), 1:3,'UniformOutput',false).'));
If the arrays are higher in dimension, I actually think a function accumulator would be quicker and easier. For example, consider the (extremely simple) function:
function acc = accumfun(f,y)
acc = f(y(1));
for k = 2:numel(y)
acc = acc + f(y(k));
end
end
Then, you can make the one-liner
g = #(x) accumfun(#(y) f(x,y),y);