I need some help with a quiete simple problem in FiPy. My goal is to simulate a fluid flowing through a concrete block while phase change.
But first of all I tried to do a simple 1D simulation assumed a fluid massflow and a constant wall temperature without any phase change.
from fipy import *
from fipy.meshes import CylindricalGrid2D, Grid1D
import matplotlib.pyplot as plt
import numpy as np
#%%
L = 1.5 #length transfer surface
bS = 0.75 #wide
AV = L * bS #transfer surface
tS0 = 350. #tWall
rhoWF = 880. #density fluid
mWF = 0.036 #mass flow
u = 5e-4 #Fluid speed
hWF = mWF / AV / rhoWF / u #height "fluid block"
nx = 50
VWF = hWF * L * bS/nx #fluid volumen
lambdaWF = 0.6 # thermal conductivity
alpha = 500. #heat transfer coefficient
tWF0 = 371.
mesh = Grid1D(dx=L/nx, nx=nx)
tWF = CellVariable(name="Fluid",
mesh=mesh,
value= tWF0,
hasOld=True)
tS = CellVariable(name="storage",
mesh=mesh,
value=tS0,
hasOld=True)
sourceWF=CellVariable(name="source Fluid", #Variable der Konvektion
mesh=mesh,
value=0.)
cvrho = CellVariable(name = 'cprho',#Fluid
mesh = mesh,
value = rhoWF * 4215.2,
hasOld = True)
tWF.constrain(tWF0, mesh.facesLeft()) #constant inlet temperature
t = 6*3600. #time
timeStepDuration = 1e2
#outflow boundary condition
outlet = mesh.facesRight
ConvCoeff = FaceVariable(mesh,value=u,rank=1)
exteriorCoeff = FaceVariable(mesh,value=0.,rank=1)
exteriorCoeff.setValue(value=ConvCoeff, where=outlet)
ConvCoeff.setValue(0., where=outlet)
residual1 = 1.
elapsedTime = 0.
tWFall = np.zeros(nx)[None,:]
while elapsedTime < t:
tWF.updateOld()
it = 0 #iterations
while residual1> 1e-2:
sourceWF.value = - AV / nx * alpha*(tWF - tS)/ cvrho / VWF #this will be a variable convection source
eq1 = HybridConvectionTerm(coeff=ConvCoeff) + TransientTerm(coeff=1.) == \
+ sourceWF\
- ImplicitSourceTerm(exteriorCoeff.divergence) \
#+ DiffusionTerm(coeff= lambdaWF / cvrho) #not necessary(?)
residual1 = eq1.sweep(dt = timeStepDuration, var = tWF)
print('res1: ' + str(residual1) )
it += 1
if it > 10:
raise ValueError (r'MaxIter reached')
elapsedTime += timeStepDuration ; print('t= ' + str(round(elapsedTime,2)))
residual1 = 1.
tWFall = np.r_[tWFall, tWF.value[None,:]] #value collection
#%% outlet fluid temperature and storage temperature
plt.plot(np.linspace(0,t/3600.,int(t/timeStepDuration)), tWFall[1:,-1], label=r'$\vartheta_{WF}$')
plt.legend()
I would expect a constant fluid outlet temperature because of the constant wall temperature and constant fluid inlet temperature. I have not defined the wall temperature as a boundary condition because some day I would like to analyse heat conduction and variable temperature gradients too. Running my mwe you can see that the fluid temperature at the outlet declines.
Could someone please help at this case?
Thanks in advance!
I changed the script around and it seem to give a constant temperature of 371.0. See this link.
The sourceWF term has been removed. I was unsure what this was for, but I think it would take time for the wall temperature to adjust to this.
The equation declaration has been moved outside the loop. This is the correct way to use FiPy, but shouldn't impact the results in this case.
Related
transform3Dpoint2D(var/px, var/py, var/pz)
//perform the rotations around each axis
//rotation around x
var/xy = cx*py - sx*pz
var/xz = sx*py + cx*pz
//rotation around y
var/yz = cy*xz - sy*px
var/yx = sy*xz + cy*px
//rotation around z
var/zx = cz*yx - sz*xy
var/zy = sz*yx + cz*xy
//return variables: x, y, how close point is (for sorting)
var/scaleRatio = 300/(300 + yz)
return list(zx*scaleRatio, zy*scaleRatio, yz)
The variables (cameraxr, camerayr, camerazr are pitch, yaw, roll respectively):
//Setup projection variables
sx = sin(cameraxr)
cx = cos(cameraxr)
sy = sin(camerayr)
cy = cos(camerayr)
sz = sin(camerazr)
cz = cos(camerazr)
When I use this code to project 3d coordinates into 2d, it works fine, but there seems to be a lack of perspective entirely. The objects behind should be smaller, and the objects on the front should be more bigger. What am I missing here? The top should also be seeen when it's moved down. It looks like this when used:
I am trying to use julai as main language for my work. But I find that this plot is different than python (Which outputs the right plot)
Here is the python code and output
import numpy as np
import math
import matplotlib.pyplot as plt
u = 9.27*10**(-21)
k = 1.38*10**(-16)
j2 = 7/2
nrr = 780
h = 1000
na = 6*10**(23)
rho = 7.842
mgd = 157.25
a = mgd
d = na*rho*u/a
m_f = []
igd = 7.0
for t in range(1,401):
while True:
h1 = h+d*nrr*igd
x2 = (7*u*h1)/(k*t)
x4 = 2*j2
q2 = (x4+1)/x4
m = abs(7*(q2*math.tanh(q2*x2)**-1 - (1/x4)*math.tanh(x2/x4)**-1))
if abs(m - igd) < 10**(-12):
break
else:
igd = m
m_f.append(abs(m))
plt.plot(range(1,401), m_f)
plt.savefig("Py_plot.pdf")
and it gives the following right plot
The right plot as expected
But when I do the same calculations in julia it gives different output than python, here is my julia code
using Plots
u = 9.27*10^(-21)
k = 1.38*10^(-16)
j2 = 7/2
nrr = 780
h = 1000
na = 6*10^(23)
rho = 7.842
mgd = 157.25
a = mgd
d = na*rho*u/a
igd = 7.0
m = 0.0
m_f = Float64[]
for t in 1:400
while true
h1 = h+d*nrr*igd
x2 = (7*u*h1)/(k*t)
x4 = 2*j2
q2 = (x4+1)/x4
m = 7*(q2*coth(rad2deg(q2*x2))-(1/x4)*coth(rad2deg(x2/x4)))
if abs(abs(m)-igd) < 10^(-10)
break
else
igd = m
end
end
push!(m_f, abs(m))
end
plot(1:400, m_f)
and this is the unexpected julia output
unexpected wrong output from julia
I wish for help....
Code:
using Plots
const u = 9.27e-21
const k = 1.38e-16
const j2 = 7/2
const nrr = 780
const h = 1000
const na = 6.0e23
const rho = 7.842
const mgd = 157.25
const a = mgd
const d = na*rho*u/a
function plot_graph()
igd = 7.0
m = 0.0
trange = 1:400
m_f = Vector{Float64}(undef, length(trange))
for t in trange
while true
h1 = h+d*nrr*igd
x2 = (7*u*h1)/(k*t)
x4 = 2*j2
q2 = (x4+1)/x4
m = abs(7*(q2*coth(q2*x2)-(1/x4)*coth(x2/x4)))
if isapprox(m, igd, atol = 10^(-10))
break
else
igd = m
end
end
m_f[t] = m
end
plot(trange, m_f)
end
Plot:
Changes for correctness:
Changed na = 6.0*10^(23) to na = 6.0e23.
Since ^ has a higher precedence than *, 10^23 is evaluated first, and since the operands are Int values, the result is also an Int. However, Int (i.e. Int64) can only hold numbers up to approximately 9 * 10^18, so 10^23 overflows and gives a wrong result.
julia> 10^18
1000000000000000000
julia> 10^19 #overflow starts here
-8446744073709551616
julia> 10^23 #and gives a wrong value here too
200376420520689664
6.0e23 avoids this problem by directly using the scientific e-notation to create a literal Float64 value (Float64 can hold this value without overflowing).
Removed the rad2deg calls when calling coth. Julia trigonometric functions by default take radians, so there's no need to make this conversion.
Other changes
Marked all the constants as const, and moved the rest of the code into a function. See Performance tip: Avoid non-constant global variables
Changed the abs(m - igd) < 10^-10 to isapprox(m, igd, atol = 10^-10) which performs basically the same check, but is clearer and more flexible (for eg. if you wanted to change to a relative tolerance rtol later).
Stored the 1:400 as a named variable trange. This is just because it's used multiple times, so it's easier to manage as a variable.
Changed m_f = Float64[] to m_f = Vector{Float64}(undef, length(trange)) (and the push! at the end to an assignment). If the size of the array is known beforehand (as it is in this case), it's better for performance to pre-allocate it with undef values and then assign to it.
Changed u and k also to use the scientific e-notation, for consistency and clarity (thanks to #DNF for suggesting the use of this notation in the comments).
I have estimated nested logit in R using the mlogit package. However, I encountered some problems when trying to estimate the marginal effect. Below is the code I implemented.
library(mlogit)
# data
data2 = read.csv(file = "neat_num_energy.csv")
new_ener2 <- mlogit.data(
data2,
choice="alter4", shape="long",
alt.var="energy_altern",chid.var="id")
# estimate model
nest2 <- mlogit(
alter4 ~ expendmaint + expendnegy |
educ + sex + ppa_power_sp + hu_price_powersupply +
hu_2price +hu_3price + hu_9price + hu_10price +
hu_11price + hu_12price,
data = data2,
nests = list(
Trad = c('Biomas_Trad', 'Solar_Trad'),
modern = c('Biomas_Modern', 'Solar_Modern')
), unscaled=FALSE)
# create Z variable
z3 <- with(data2, data.frame(
expendnegy = tapply(expendnegy, idx(nest2,2), mean),
expendmaint= tapply(expendmaint, idx(nest2,2), mean),
educ= mean(educ),
sex = mean(sex),
hu_price_powersupply = mean(hu_price_powersupply),
ppa_power_sp = mean(ppa_power_sp),
hu_2price = mean(hu_2price),
hu_3price = mean(hu_3price),
hu_9price = mean(hu_9price),
hu_10price = mean(hu_10price),
hu_11price = mean(hu_11price),
ppa_power_sp = mean(ppa_power_sp),
hu_12price = mean(hu_12price)
))
effects(nest2, covariate = "sex", data = z3, type = "ar")
#> ** Error in Solve.default (H, g[!fixed]): Lapack routine dgesv: #> system is exactly singular:U[6,6] =0.**
My data is in long format with expendmaint and expendnegy being the only alternative specific while every other variable is case specific.
altern4 is a nominal variable representing each alternative
At Sunrise sun is supposed to be at the horizon (ie 0 degree altitude at east).
but if I check ecliptic longitude of:
the sun at sunrise and
from_altaz() of zero degrees east
I get different values for both. But is it not supposed to be the same?
from skyfield import almanac, api
from pytz import timezone
from datetime import datetime, timedelta
ts = api.load.timescale(builtin=True)
eph = api.load('de421.bsp')
earth = eph['EARTH']
sun = eph['SUN']
tz = timezone('UTC')
# 2019-12-23 01:23:58.273000+00:00
testTime = datetime(year=2019, month=12, day=23, hour=1,
minute=23, second=58)
testTime = tz.localize(testTime)
lat = '33.775867N'
lon = '84.39733E'
observer = api.Topos(lat, lon)
t0 = ts.utc(testTime)
t1 = ts.utc(testTime + timedelta(days=1))
t, y = almanac.find_discrete(t0, t1, almanac.sunrise_sunset(eph, observer))
for i, j in zip(t, y):
if j:
print('Sunrise : ', i.utc_datetime().astimezone(tz), j)
observer = earth + api.Topos(lat, lon)
zeroDegreeEast = observer.at(t0).from_altaz(alt_degrees=0, az_degrees=90)
_, lonAtHorizon, _ = zeroDegreeEast.ecliptic_latlon(epoch=t0)
sunAtSunRise = observer.at(t0).observe(sun).apparent()
_, lonSun, _ = sunAtSunRise.ecliptic_latlon(epoch=t0)
print('TestDate: ', testTime.isoformat())
print(lonAtHorizon)
print(lonSun)
Sunrise : 2019-12-23 01:23:58.273000+00:00 True
TestDate: 2019-12-23T01:23:58+00:00
288deg 05' 48.3"
270deg 53' 48.0"
what am I missing?
Is there anything I could do to get the same values?
There are two additional effects that your script does not consider.
By definition, sunrise is when the center of the sun is 0.8333 degrees below the horizon, because it's half a degree wide and additionally its image is refracted upward and is visible ahead of schedule because of the atmosphere.
The sun never rises directly to the east, because of the seasons.
You can ask for the Sun's position on the horizon at the moment of sunrise that you've computed to account for the second effect:
alt, az, distance = ((observer + eph['earth']).at(t[0])
.observe(eph['sun']).apparent().altaz())
print(alt, az)
print(az.degrees)
Which prints:
-00deg 49' 59.9" 117deg 57' 17.6"
117.95487601490409
or about 27° south of directly east. You can then account for the first effect by adjusting the altaz you are computing:
zeroDegreeEast = observer.at(t0).from_altaz(alt_degrees=-0.8333, az_degrees=117.954)
The result should very closely match what you expect.
I'm try to run an ocean temperature model for 25 years using the explicit method (parabolic differential equation).
If I run for a year a = 3600 or five years a = 18000 it works fine.
However, when I run it for 25 years a = 90000 it crashes.
a is the amount of time steps used. And a year is considered to be 360 days. The time step is 4320 seconds, delta_t = 4320..
Here is my code:
program task
!declare the variables
implicit none
! initial conditions
real,parameter :: initial_temp = 4.
! vertical resolution (delta_z) [m], vertical diffusion coefficient (av) [m^2/s], time step delta_t [s]
real,parameter :: delta_z = 2., av = 2.0E-04, delta_t = 4320.
! gamma
real,parameter :: y = (av * delta_t) / (delta_z**2)
! horizontal resolution (time) total points
integer,parameter :: a = 18000
!declaring vertical resolution
integer,parameter :: k = 101
! declaring pi
real, parameter :: pi = 4.0*atan(1.0)
! t = time [s], temp_a = temperature at upper boundary [°C]
real,dimension(0:a) :: t
real,dimension(0:a) :: temp_a
real,dimension(0:a,0:k) :: temp
integer :: i
integer :: n
integer :: j
t(0) = 0
do i = 1,a
t(i) = t(i-1) + delta_t
end do
! temperature of upper boundary
temp_a = 12. + 6. * sin((2. * t * pi) / 31104000.)
temp(:,0) = temp_a(:)
temp(0,1:k) = 4.
! Vertical resolution
do j = 1,a
do n = 1,k
temp(j,n) = temp(j-1,n) + (y * (temp(j-1,n+1) - (2. * temp(j-1,n)) + temp(j-1,n-1)))
end do
temp(:,101) = temp(:,100)
end do
print *, temp(:,:)
end program task
The variable a is on line 11 (integer,parameter :: a = 18000)
As said, a = 18000 works, a = 90000 doesn't.
At 90000 get I get:
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.
Backtrace for this error:
RUN FAILED (exit value 1, total time: 15s)
I'm using a fortran on windows 8.1, NetBeans and Cygwin (which has gfortran built in).
I'm not sure if this problem is caused through bad compiler or anything else.
Does anybody have any ideas to this? It would help me a lot!
Regards
Take a look at the following lines from your code:
integer,parameter :: k = 101
real,dimension(0:a,0:k) :: temp
integer :: n
do n = 1,k
temp(j,n) = temp(j-1,n) + (y * (temp(j-1,n+1) - (2. * temp(j-1,n)) + temp(j-1,n-1)))
end do
Your array temp has bounds of 0:101, you loop n from 1 to 101 where in iteration n=101 you access temp(j-1,102), which is out of bounds.
This means you are writing to whatever memory lies beyond temp and while this makes your program always incorrect, it is only causing a crash sometimes which depends on various other things. Increasing a triggers this because column major ordering of your array means k changes contiguously and is strided by a, and as a increases your out of bounds access of the second dimension is further in memory beyond temp changing what is getting overwritten by your invalid access.
After your loop you set temp(:,101) = temp(:,100) meaning there is no need to calculate temp(:,101) in the above loop, so you can change its loop bounds from
do n = 1,k
to
do n = 1, k-1
which will fix the out of bounds access on temp.