diff --git a/src/Makefile b/src/Makefile index 57c579281..f43b6e839 100644 --- a/src/Makefile +++ b/src/Makefile @@ -235,8 +235,8 @@ SOURCES = mcfost_env.f90 parameters.f90 constants.f90 healpix_mod.f90 sha.f90 me read_DustEM.f90 Temperature.f90 density.f90 read_opacity.f90 scattering.f90 \ read_fargo3d.f90 hdf5_utils.f90 utils_hdf5.f90 read_athena++.f90 \ readVTK.f90 read_idefix.f90 read_pluto.f90 voigts.f90 \ - read1d_models.f90 read_spherical_grid.f90 \ coated_sphere.f90 dust_prop.f90 molecular_emission.f90 PAH.f90 \ + read1d_models.f90 read_spherical_grid.f90 \ input.f90 benchmarks.f90 atom_type.f90 \ wavelengths_gas.f90 broad.f90 \ read_param.f90 dust_ray_tracing.f90 uplow.f90 abo.f90 \ diff --git a/src/cylindrical_grid.f90 b/src/cylindrical_grid.f90 index 74af4cd8b..07c022809 100644 --- a/src/cylindrical_grid.f90 +++ b/src/cylindrical_grid.f90 @@ -385,7 +385,6 @@ subroutine define_cylindrical_grid() !redifine and use the r grid read if (lsphere_model) then - !tab_r = r_lim ? tab_r(:) = pluto%x1(1:n_rad+1) !1 -> n_rad + 1 r_lim(:) = pluto%x1(:) !from Rmin to Rmax, 0 to n_rad r_lim_2(:) = r_lim(:) * r_lim(:) @@ -537,27 +536,18 @@ subroutine define_cylindrical_grid() ! test theta grid endif -! write(*,*) "wlim1=", w_lim -! write(*,*) "tlim1=", theta_lim -! write(*,*) "dc1=", dcos_theta -! write(*,*) "tantlim1=", tan_theta_lim - !redifine and use the theta grid read, still spherical if (lsphere_model) then - w_lim(:) = pluto%x2(nz+1:1:-1) !one more cell - theta_lim(:) = asin(w_lim) - ! write(*,*) size(pluto%x2), nz, size(w_lim), size(theta_lim) + !pluto%x2 goes from the max value of theta (pi/2 or pi if 3D) to 0. + !It has been re-ordered such that it goes from 0 to pi/2 (or pi): + ! %x2(1) = 0; %x2(nz+1) = pi/2 even in 3d (%x2(2*nz+1)=pi). + theta_lim(:) = pluto%x2(1:nz+1) + w_lim(:) = sin(theta_lim) tan_theta_lim(0) = 1.0e-10_dp tan_theta_lim(nz) = 1.e30_dp tan_theta_lim(1:nz-1) = tan(theta_lim(1:nz-1)) dcos_theta(1:nz) = w_lim(1:nz) - w_lim(0:nz-1) - - ! write(*,*) "w=",w_lim - ! write(*,*) "t=",theta_lim - ! write(*,*) dcos_theta - ! write(*,*) tan_theta_lim - endif do i=1, n_rad @@ -597,11 +587,9 @@ subroutine define_cylindrical_grid() endif enddo !k - !redifine and use the phi grid from file - ! write(*,*) "phi1=", phi_grid_tmp - ! write(*,*) "tan_phi1=", tan_phi_lim - ! write(*,*) size(tan_phi_lim), n_az, size(pluto%x3), size(phi_grid_tmp) - if (lsphere_model) then + !handle 2.5d (l3d but n_az==1) and pure 3d (n_az > 1). + if ((lsphere_model).and.(n_az>1)) then + !redifine and use the phi grid from file phi_grid_tmp(:) = 0.5*(pluto%x3(2:n_az+1) + pluto%x3(1:n_az)) ! do k=1, n_az ! phi = pluto%x3(k+1) @@ -616,9 +604,6 @@ subroutine define_cylindrical_grid() elsewhere tan_phi_lim = tan(pluto%x3(2:n_az+1)) endwhere - ! write(*,*) "phi=", phi_grid_tmp - ! write(*,*) "tan_phi=", tan_phi_lim - ! stop endif V(:,:) = V(:,:) * 0.5 / real(n_az) diff --git a/src/dust_transfer.f90 b/src/dust_transfer.f90 index b378287a7..2821b1b84 100644 --- a/src/dust_transfer.f90 +++ b/src/dust_transfer.f90 @@ -761,7 +761,8 @@ subroutine transfert_poussiere() call ecriture_temperature(1) call ecriture_sed(1) - if (lapprox_diffusion.and.l_is_dark_zone.and.(lemission_mol.or.lprodimo.or.lML.or.lforce_diff_approx)) then + if (lapprox_diffusion.and.l_is_dark_zone.and.& + (lemission_mol.or.lprodimo.or.lML.or.lforce_diff_approx.or.lemission_atom)) then call Temp_approx_diffusion_vertical() ! call Temp_approx_diffusion() call diffusion_approx_nLTE_nRE() diff --git a/src/gas/atom_transfer.f90 b/src/gas/atom_transfer.f90 index c7c823b9b..c4ae653e5 100644 --- a/src/gas/atom_transfer.f90 +++ b/src/gas/atom_transfer.f90 @@ -16,7 +16,7 @@ module atom_transfer use elecdensity, only : solve_ne, write_electron, read_electron use grid, only : T, vturb,nHtot, nHmin, pos_em_cellule, lcalc_ne, move_to_grid, vfield3d, icompute_atomRT, & ne, Voronoi, r_grid, phi_grid, z_grid - use lte, only : ltepops_atoms, ltepops_atoms_1, print_pops, LTEpops_atom_loc, LTEpops_H_loc, nH_minus + use lte, only : ltepops_atoms, LTEpops_atom_loc, LTEpops_H_loc, nH_minus use atom_type, only : atoms, atomtype, n_atoms, nactiveatoms, activeAtoms, passiveAtoms, npassiveatoms, & hydrogen, helium, adjust_cswitch_atoms, & maxval_cswitch_atoms, lcswitch_enabled, vbroad @@ -25,7 +25,7 @@ module atom_transfer use opacity_atom, only : alloc_atom_opac, Itot, psi, dealloc_atom_opac, xcoupling, write_opacity_emissivity_bin, & lnon_lte_loop, vlabs, calc_contopac_loc, set_max_damping, deactivate_lines, activate_lines, & activate_continua, deactivate_continua - use see, only : ngpop, Neq_ng, ngpop, alloc_nlte_var, dealloc_nlte_var, frac_limit_pops, & + use see, only : ngpop, Neq_ng, ngpop, alloc_nlte_var, dealloc_nlte_var, small_nlte_fraction, & init_rates, update_populations, accumulate_radrates_mali, write_rates, init_radrates_atom use optical_depth, only : integ_ray_atom use utils, only : cross_product, gauss_legendre_quadrature, progress_bar, rotation_3d, vacuum2air, & @@ -39,6 +39,11 @@ module atom_transfer use messages, only : error, warning use voigts, only : Voigt use broad, only : line_damping + use density, only : densite_pouss + use temperature, only : Tdust + use mem, only : clean_mem_dust_mol, realloc_dust_atom, deallocate_em_th_mol,emissivite_dust + use dust_prop, only : prop_grains, opacite, init_indices_optiques, kappa_abs_lte, kappa, kappa_factor + use scattering use healpix_mod !$ use omp_lib @@ -235,7 +240,7 @@ subroutine nlte_loop_mali() call alloc_nlte_var(one_ray,mem=mem_alloc_local) ! --------------------------- OUTER LOOP ON STEP --------------------------- ! - + precision = dpops_max_error step_loop : do etape=etape_start, etape_end write(*,*) "" @@ -248,15 +253,8 @@ subroutine nlte_loop_mali() write(*,'(" ****-> Using "(1I8)" pixels for healpix, resolution of "(1F12.3)" degrees")') n_rayons, & healpix_angular_resolution(healpix_lorder) call healpix_sphere(healpix_lorder,xmu,xmux,xmuy) - if (etape_end > 1) then - !use that etape as an initial solution for step 2 - precision = 1d-1 - else - precision = dpops_max_error - endif conv_speed_limit = conv_speed_limit_healpix else if (etape==2) then - precision = dpops_max_error n_rayons = N_rayons_mc write(*,'(" ****-> Using "(1I4)" rays for Monte-Carlo step, ~resolution of "(1F12.3)" degrees")') n_rayons, & 360.0 * sqrt(pi/real(n_rayons))/pi @@ -359,7 +357,7 @@ subroutine nlte_loop_mali() !$ id = omp_get_thread_num() + 1 l_iterate = (icompute_atomRT(icell)>0) stream(id) = init_sprng(gtype, id-1,nb_proc,seed,SPRNG_DEFAULT) - if( (diff_loc(icell) < 1d-1 * precision).and..not.lcswitch_enabled ) cycle + if( (diff_loc(icell) < 5d-2 * precision).and..not.lcswitch_enabled ) cycle if (l_iterate) then @@ -447,7 +445,7 @@ subroutine nlte_loop_mali() !$omp atomic n_cells_done = n_cells_done + 1 ! n_cells_remaining = size(pack(diff_loc, & - ! mask=(diff_loc < 1d-1 * precision))) + ! mask=(diff_loc < 5d-2 * precision))) if (real(n_cells_done) > 0.02*ibar*n_cells) then call progress_bar(ibar) !$omp atomic @@ -480,7 +478,7 @@ subroutine nlte_loop_mali() endif endif if (lng_turned_on) then - if (unconverged_fraction < 15.0) lng_turned_on = .false. + if (unconverged_fraction < 10.0) lng_turned_on = .false. endif endif !***********************************************************! @@ -592,12 +590,12 @@ subroutine nlte_loop_mali() ! write(*,'(" NEW ne(min)="(1ES16.8E3)" m^-3 ;ne(max)="(1ES16.8E3)" m^-3")') & ! minval(ne,mask=(icompute_atomRT>0)), maxval(ne) ! write(*,*) '' - ! if ((dne < 1d-2 * precision).and.(.not.lcswitch_enabled)) then - ! !Or compare with 3 previous values of dne ? that should be below 1e-2 precision - ! !Do we need to restart it eventually ? - ! write(*,*) " *** stopping electronic density convergence at iteration ", n_iter - ! n_iterate_ne = 0 - ! endif + if ((dne < 1d-4 * precision).and.(.not.lcswitch_enabled)) then + !Do we need to restart it eventually ? + write(*,*) " *** dne", dne + write(*,*) " *** stopping electronic density convergence at iteration ", n_iter + n_iterate_ne = 0 + endif end if !***********************************************************! @@ -628,7 +626,7 @@ subroutine nlte_loop_mali() at => ActiveAtoms(nact)%p do ilevel=1,at%Nlevel - if ( ngpop(ilevel,nact,icell,1) >= frac_limit_pops * at%Abund*nHtot(icell) ) then + if ( ngpop(ilevel,nact,icell,1) >= small_nlte_fraction * at%Abund*nHtot(icell) ) then dN1 = abs(1d0-at%n(ilevel,icell)/ngpop(ilevel,nact,icell,1)) dN = max(dN1, dN) dM(nact) = max(dM(nact), dN1) @@ -726,10 +724,7 @@ subroutine nlte_loop_mali() diff_old = diff conv_acc = conv_speed - if ((diff < precision).and.(.not.lcswitch_enabled))then!maxval_cswitch_atoms()==1.0_dp - ! if ( (unconverged_fraction < 3.0).and.maxval_cswitch_atoms()==1.0_dp)then - ! if ( ((unconverged_fraction < 3.0).and.maxval_cswitch_atoms()==1.0_dp).or.& - ! ((diff < precision).and.maxval_cswitch_atoms()==1.0_dp) )then + if ((diff < precision).and.(.not.lcswitch_enabled))then if (lprevious_converged) then lconverged = .true. else @@ -783,8 +778,8 @@ subroutine nlte_loop_mali() !force convergence if there are only few unconverged cells remaining ! if ((n_iter > maxIter/4).and.(unconverged_fraction < 5.0)) then - if ( (n_iter > maxIter/4).and.(unconverged_fraction < 5.0).or.& - ((unconverged_fraction < 5.0).and.(time_nlte + time_iteration >= 0.5*safe_stop_time)) ) then + if ( (n_iter > maxIter/4).and.(unconverged_fraction < 3.0).or.& + ((unconverged_fraction < 3.0).and.(time_nlte + time_iteration >= 0.5*safe_stop_time)) ) then write(*,'("WARNING: there are less than "(1F6.2)" % of unconverged cells after "(1I4)" iterations")') & unconverged_fraction, n_iter write(*,*) " -> forcing convergence" @@ -834,7 +829,6 @@ subroutine nlte_loop_mali() if (n_iterate_ne > 0) then write(*,'("ne(min)="(1ES17.8E3)" m^-3 ;ne(max)="(1ES17.8E3)" m^-3")') minval(ne,mask=icompute_atomRT>0), maxval(ne) - write(*,'("nH/ne "(1ES13.5E3, 1ES13.5E3))') maxval(nHtot/ne,mask=nhtot*ne>0), minval(nHtot/ne,mask=nHtot*ne>0) call write_electron endif @@ -1030,7 +1024,7 @@ subroutine setup_image_grid() !keep somewhere tab_lambda_sed = tab_lambda because tab_lambda is overwritten in non-LTE integer :: kr, nat - call deallocate_wavelengths_gasrt(tab_lambda) + call deallocate_wavelengths_gasrt() call dealloc_atom_opac() call init_directions_ray_tracing() @@ -1057,8 +1051,9 @@ subroutine setup_image_grid() call make_wavelengths_raytracing(tab_lambda_nm) endif n_lambda = size(tab_lambda_nm) - tab_lambda = tab_lambda_nm * nm_to_m!micron + tab_lambda = tab_lambda_nm * m_to_km ! [mic] + call init_dust_atom() !even in 1d mode. call alloc_atom_opac(n_lambda, tab_lambda_nm, .true.) call allocate_atom_maps() if (laccretion_shock) then @@ -1089,6 +1084,14 @@ subroutine atom_line_transfer() tab_lambda_sed = tab_lambda endif + !associate the temperature in the regions where the dusty of dust is non-zero (if any) to the dust + !temperature. + if (ldust_atom) then + call deallocate_em_th_mol() + lscatt_ray_tracing = .false. ! tmp : scatt ray-tracing has no sense yet for atomic emssion + call init_dust_temperature() + endif + !before LTE pops, electronic density and eventually chemical equilibrium. !read atomic models call read_atomic_Models() @@ -1137,10 +1140,12 @@ subroutine atom_line_transfer() !Even if we split the transfer un group of wavelength that's probably best to keep indexes. !otherwise, we pass group wavelength and check contributing opacities for each group !but it is like indexes at the end? - deallocate(tab_lambda, tab_lambda_inf, tab_lambda_sup, tab_delta_lambda) + call deallocate_wavelengths_gasrt() call make_wavelengths_nlte(tab_lambda_nm,vmax_overlap=v_char) n_lambda = size(tab_lambda_nm) - tab_lambda = tab_lambda_nm * m_to_km + tab_lambda = tab_lambda_nm * m_to_km ! [micron] + + call init_dust_atom() ! !allocate quantities in space and for this frequency grid call alloc_atom_opac(n_lambda, tab_lambda_nm, .false.) @@ -1199,6 +1204,87 @@ subroutine atom_line_transfer() return end subroutine atom_line_transfer + subroutine init_dust_temperature() + !lowering too much the treshold might create some convergence issues in the non-LTE pops or in + ! the electronic density calculations (0 division mainly for low values). + real(kind=dp), parameter :: T_formation = 2000.0 ! [K] + logical, dimension(:), allocatable :: ldust + allocate(ldust(n_cells)); ldust = (sum(densite_pouss,dim=1)>0.0_dp) + write(*,*) " *** Associating the dust temperature in the model..." + !force dust_sublimation + ! TO DO: + ! call sublimate_dust() + write(*,*) "Tdust:", maxval(Tdust), minval(Tdust) + write(*,*) "Tdust (rho_dust >0 ):", maxval(Tdust,ldust), minval(Tdust,ldust) + !If the dust temperature is above T_formation the gas will follow that temperature + !and atomic opacities will be considered. + where(ldust) T(:) = Tdust(:) + if (any(T < T_formation)) call warning('(atom_transfer) Setting the gas transparent where T < 2000 K.') + where (T < T_formation) icompute_atomRT = 0 + !or set the atomic gas temperature to 0 in dust regions ? + if (any(icompute_atomRT>0)) then + write(*,*) "T:", real(maxval(T,icompute_atomRT>0)), real(minval(T,icompute_atomRT>0)) + write(*,*) "T (rho_dust = 0):", real(maxval(T,(icompute_atomRT>0).and..not.ldust)), & + real(minval(T,(icompute_atomRT>0).and..not.ldust)) + else + call warning("(init_dust_temperature) The (atomic) gas is all transparent !") + endif + write(*,*) " *** done." + deallocate(ldust) + return + end subroutine init_dust_temperature + + subroutine init_dust_atom() + !see mol_transfer.f90 / init_dust_mol() for TO DOs. + integer :: la, p_lambda + integer, target :: icell + integer, pointer :: p_icell + + if (.not.ldust_atom) then + !still add kappa etc in the total contopac to not care about testing if dust + !in the propagation (+limit_mem options). + return ! no dust + endif + + ! On n'est interesse que par les prop d'abs : pas besoin des matrices de mueller + ! -> pas de polarisation, on utilise une HG + scattering_method=1 ; lscattering_method1 = .true. ; p_lambda = 1 + aniso_method = 2 ; lmethod_aniso1 = .false. + lsepar_pola = .false. + ltemp = .false. + lmono = .true. ! equivalent au mode sed2 + + if (lvariable_dust) then + p_icell => icell + else + p_icell => icell_ref + endif + + call realloc_dust_atom() + + !BEWARE: by default tab_lambda is associated to tab_lambda_nm which can be long. + ! Consider using tab_lambda_cont and then interpolate on tab_lambda_nm + write(*,*) " *** initialising optical indices" + call init_indices_optiques() + + ! Computing optical dust properties + ! TO DO: limit_mem options. + write(*,*) " *** Computing dust properties for", n_lambda, "wavelengths..." + do la=1, n_lambda !works also for ray-traced lines + call prop_grains(la) + call opacite(la, la, no_scatt=.true.) + enddo + + do icell=1, n_cells + if (sum(densite_pouss(:,icell)) <= 0.0_dp) cycle + emissivite_dust(:,icell) = kappa_abs_LTE(p_icell,:) * kappa_factor(icell) * & + m_to_AU * Bpnu(n_lambda,tab_lambda_nm,T(icell)) ! [W m^-3 Hz^-1 sr^-1] + enddo + + call clean_mem_dust_mol() + write(*,*) " *** done." + return + end subroutine init_dust_atom subroutine intensite_pixel_atom(id,ibin,iaz,n_iter_min,n_iter_max,ipix,jpix,pixelcorner,pixelsize,dx,dy,u,v,w) ! -------------------------------------------------------------- ! @@ -1292,6 +1378,7 @@ subroutine intensite_pixel_atom(id,ibin,iaz,n_iter_min,n_iter_max,ipix,jpix,pixe ! Flux out of a pixel in W/m2/Hz/pix normF = ( pixelsize / (distance*pc_to_AU) )**2 + !to nu.Fnu -> normF * c_light / nm_to_m; flux -> flux / tab_lambda_nm if (RT_line_method==1) then Flux_total(:,ibin,iaz,id) = Flux_total(:,ibin,iaz,id) + I0(:) * normF @@ -1622,7 +1709,7 @@ subroutine spectrum_1d() allocate(cos_theta(Nimpact), weight_mu(Nimpact), p(Nimpact)) !prepare wavelength grid and indexes for continua and lines !including max extension due to velocity shiftS. - call deallocate_wavelengths_gasrt(tab_lambda) + call deallocate_wavelengths_gasrt() call dealloc_atom_opac() if (lsed) then call make_wavelengths_flux(tab_lambda_sed,.true.) diff --git a/src/gas/atom_type.f90 b/src/gas/atom_type.f90 index e2161b3e3..eeaff140a 100644 --- a/src/gas/atom_type.f90 +++ b/src/gas/atom_type.f90 @@ -87,6 +87,7 @@ module atom_type logical :: NLTEpops, set_ltepops real(kind=dp), allocatable :: Gamma(:,:,:), dgdne(:,:,:) !derivative of Gamma to ne (n_iterate_ne>0) real(kind=dp), dimension(:,:), pointer :: n, nstar + real(kind=dp), dimension(:,:), allocatable :: ni_on_nj_star ! real(kind=dp), dimension(:,:) :: phi_T !such that nexphi_T = ni/nj type (AtomicLine), allocatable, dimension(:) :: lines type (AtomicContinuum) , allocatable, dimension(:) :: continua diff --git a/src/gas/collision_atom.f90 b/src/gas/collision_atom.f90 index 26ca38fbb..de3026937 100644 --- a/src/gas/collision_atom.f90 +++ b/src/gas/collision_atom.f90 @@ -37,8 +37,9 @@ subroutine collision_rates_hydrogen_loc(id,icell) call Johnson_CI(icell, CI) !bound-free i->Nlevel j = hydrogen%Nlevel do i=1, j-1 - !ni_on_nj_star = ne(icell) * phi_T(icell, hydrogen%g(i)/hydrogen%g(hydrogen%Nlevel), hydrogen%E(hydrogen%Nlevel)-hydrogen%E(i)) - ni_on_nj_star = hydrogen%nstar(i,icell) / hydrogen%nstar(j,icell) + ! ni_on_nj_star = ne(icell) * phi_T(T(icell), hydrogen%g(i), hydrogen%g(j), hydrogen%E(j)-hydrogen%E(i)) + ! ni_on_nj_star = hydrogen%nstar(i,icell) / hydrogen%nstar(j,icell) + ni_on_nj_star = hydrogen%ni_on_nj_star(i,icell) kr = hydrogen%ij_to_trans(i,j) - hydrogen%Nline hydrogen%continua(kr)%Cij(id) = ne(icell) * CI(i) !deriv = Cij/ne hydrogen%continua(kr)%Cji(id) = ne(icell) * CI(i) * ni_on_nj_star !deriv = 2*Cji/ne @@ -53,7 +54,11 @@ subroutine collision_rates_hydrogen_loc(id,icell) do i=1,hydrogen%Nlevel do j=i+1,hydrogen%Nlevel-1 kr = hydrogen%ij_to_trans(i,j) - ni_on_nj_star = hydrogen%nstar(i,icell) / hydrogen%nstar(j,icell) + if (hydrogen%nstar(j,icell)>0.0) then + ni_on_nj_star = hydrogen%nstar(i,icell) / hydrogen%nstar(j,icell) + else + ni_on_nj_star = 0.0 !correct ? + endif hydrogen%lines(kr)%Cij(id) = ne(icell) * CE(i,j) !deriv = Cij/ne hydrogen%lines(kr)%Cji(id) = ne(icell) * CE(i,j) * ni_on_nj_star !deriv = 2*Cji/ne enddo diff --git a/src/gas/electron_density.f90 b/src/gas/electron_density.f90 index c3cf1610c..9e5b45871 100644 --- a/src/gas/electron_density.f90 +++ b/src/gas/electron_density.f90 @@ -39,9 +39,7 @@ module elecdensity integer, parameter :: N_MAX_ELEMENT=26!stops at Iron. real(kind=dp) :: min_f_HII, max_f_HII character(len=10) :: ne_filename = "ne.fits.gz" - real(kind=dp), parameter :: ne_min_limit = 1d-10 ! ne_min_limit * nHtot = electron per cubic meters. - !if ne is below ne_min_limit, we set it to 1 electron per cc (ne_one) ! - real(kind=dp), parameter :: ne_one = 1.0_dp !one electron per cubic meters. + real(kind=dp), parameter :: ne_small = 1d3 ! [m^-3] !Introducing a lock for ne iterations with icompute_atomRT == 2 !the transfer (and opacity) is solved for icompute_atomRT > 0 @@ -397,7 +395,7 @@ subroutine solve_ne_loc(k,ne_init) if (is_nan_infinity(ne(k))>0) then write(*,*) niter, "icell=",k," T=",T(k)," nH=",nHtot(k), "dne = ",dne, " ne=",ne(k), " nedag = ", ne_old, " sum=", sum call error("electron density is nan or inf!") - else if (ne(k) <= 0.0) then + else if (ne(k) < 0.0) then write(*,*) niter, "icell=",k," T=",T(k)," nH=",nHtot(k), " ne0=", ne_init write(*,*) "dne = ",dne, " ne=",ne(k), " nedag = ", ne_old, "sum=", sum call error("Negative electron density!") @@ -405,13 +403,14 @@ subroutine solve_ne_loc(k,ne_init) niter = niter + 1 if (dne <= MAX_ELECTRON_ERROR) then - if (ne(k) < nHtot(k)*ne_min_limit) then - write(*,*) " (Solve ne) ne < ne_min_limit, setting cell transparent!", k - write(*,*) "T=", T(k), ' nHtot=', nHtot(k), " ne=", ne(k) - write(*,*) " -> setting ne to ", ne_one, " m^-3" - ne(k) = ne_one - icompute_atomRT(k) = 0 - endif + !set transparent ? dark ? + ! if (ne(k) < ne_small) then + ! write(*,*) " (Solve ne) ne < ne_small at cell",k!, " ; setting cell transparent!" + ! write(*,*) "T=", T(k), ' nHtot=', nHtot(k), " ne=", ne(k) + ! write(*,*) " -> setting ne to ", ne_small, " m^-3" + ! ne(k) = ne_small + ! icompute_atomRT(k) = 0 + ! endif exit else if (niter >= N_MAX_ELECTRON_ITERATIONS) then if (dne > 0.0) then !shows the warning only if dne is actually greater than 1 @@ -527,18 +526,11 @@ subroutine solve_ne(initial, verbose, epsilon) !if Abund << 1. and chiM << chiH then ! ne (H+M) = ne(H) + ne(M) ne0 = ne0 + ne_oldM - if (ne0 < nHtot(k)*ne_min_limit) ne0 = ne_one + ne0 = max(ne0, ne_small) end if - if (t(k) > 1d6) then - !fully ionised - ne(k) = 1.2 * nHtot(k) - else - !Loop starts - call solve_ne_loc(k, ne0) - endif - + call solve_ne_loc(k, ne0) if (abs(1.0_dp - ne0 / ne(k)) > eps_id(id)) then eps_id(id) = abs(1.0_dp - ne0 / ne(k)) @@ -560,7 +552,7 @@ subroutine solve_ne(initial, verbose, epsilon) !$omp end parallel call progress_bar(50) epsilon = maxval(eps_id) - ik_max = ik_max_id(locate(eps_id, epsilon)) + ik_max = max(ik_max_id(locate(eps_id, epsilon)),1) if (verbose) then write(*,*) " ---------------------------------------------------- " @@ -569,11 +561,11 @@ subroutine solve_ne(initial, verbose, epsilon) write(*,*) " T = ", T(ik_max)," nH = ", nHtot(ik_max) write(*,*) " " write(*,'("Ionisation fraction of HII "(1ES13.5E3, 1ES13.5E3))') max_f_HII, min_f_HII - ! write(*,*) nHtot(locate(ne/(1d-50+nHtot),maxval(ne/(1d-50+nHtot)))) - write(*,'("nH/ne "(1ES13.5E3, 1ES13.5E3))') maxval(nHtot/ne,mask=ne>0), minval(nHtot/ne,mask=ne>0) - ! call show_electron_given_per_elem(0, 0, max_fjk) + ! write(*,'("nH/ne "(1ES13.5E3, 1ES13.5E3))') maxval(nHtot/ne,mask=ne>0), minval(nHtot/ne,mask=ne>0) + ! call show_electron_given_per_elem(0, 0, max_fjk) write(*,*) " ---------------------------------------------------- " endif + ! where (ne < ne_small) ne = 0.0 !that's because I use the sum as a variable so the function doesn't exist. do k=2, nb_proc diff --git a/src/gas/elements_type.f90 b/src/gas/elements_type.f90 index fcf347f5a..4f3b11404 100644 --- a/src/gas/elements_type.f90 +++ b/src/gas/elements_type.f90 @@ -43,6 +43,7 @@ module elements_type real(kind=dp), parameter :: phi_min_limit = 1d-100!tiny_dp!1d-100 !1d-50, tiny_dp integer :: Max_ionisation_stage + real(kind=dp), parameter :: CI = 0.5*(HP**2 / (2.*PI*mel*kb))**1.5 ! Atomic properties constants @@ -358,6 +359,17 @@ function get_pf(elem, j, temp) result (Uk) return end function get_pf + function phi_T(temp, gi, gj, dE)!Ui_on_Uj, + !Similar to phi_jl with less tests. + !Mainly here for on-fly calculations of ni*/nj* for continua which is + !also equal to ne * phi_T. This prevents divding by 0. + real(kind=dp) :: phi_T + real(kind=dp), intent(in) :: temp, dE, gi, gj!, Ui_on_Uj + + phi_T = gi/gj * CI * temp**(-1.5) * exp(dE / ( kb*temp ))!* Ui_on_Uj * + + return + end function phi_T function phi_jl(temp, Ujl, Uj1l, ionpot) ! -------------------------------------------------------------- ! @@ -376,10 +388,8 @@ function phi_jl(temp, Ujl, Uj1l, ionpot) ! -------------------------------------------------------------- ! real(kind=dp), intent(in) :: temp real(kind=dp) :: phi_jl - real(kind=dp) :: C1, expo + real(kind=dp) :: expo real(kind=dp), intent(in) :: Ujl, Uj1l, ionpot - C1 = 0.5*(HP**2 / (2.*PI*mel*kb))**1.5 - !C1 = (HPLANCK**2 / (2.*PI*M_ELECTRON*KBOLTZMANN))**1.5 !!ionpot = ionpot * 100.*HPLANCK*CLIGHT !cm1->J !! -> avoiding dividing by big numbers causing overflow. @@ -388,7 +398,7 @@ function phi_jl(temp, Ujl, Uj1l, ionpot) if (ionpot/(kb*temp) >= 600d0) expo = huge_dp !if exp(300) it means phi is "infinity", exp(300) == 2e130 so that's enough - phi_jl = C1 * (Ujl / Uj1l) * expo / (temp**1.5 + tiny_dp) + phi_jl = CI * (Ujl / Uj1l) * expo / (temp**1.5 + tiny_dp) if (phi_jl < phi_min_limit) phi_jl = 0d0 !tiny_dp ! or phi = 0d0 should be more correct ? ! but test to not divide by 0 diff --git a/src/gas/io_atom.f90 b/src/gas/io_atom.f90 index 2eebd20e1..37d091493 100644 --- a/src/gas/io_atom.f90 +++ b/src/gas/io_atom.f90 @@ -107,6 +107,9 @@ subroutine read_model_atom(atomunit, atom) allocate(parse_labs(atom%Nlevel)) !atomic level's population at LTE (n^*) allocate(atom%nstar(atom%Nlevel,n_cells)) + !ratio of continuum lower level to upper level + allocate(atom%ni_on_nj_star(atom%Nlevel-1,n_cells)); atom%ni_on_nj_star = 0.0_dp + ! write(*,*) "size ni*/nj*=", sizeof(atom%ni_on_nj_star)/1024.**3, " GB" !total number of transitions for this model atom%Ntr = atom%Nline + atom%Ncont !-> to remove tab_trans ?? @@ -151,7 +154,6 @@ subroutine read_model_atom(atomunit, atom) !it cannot be used as a loop index :-) !read bounb-bound (line) transitions - write(*,*) "MAGNETIC POLARIZATION REMOVED AT THE MOMENT" allocate(atom%lines(atom%Nline)) do kr=1,atom%Nline @@ -258,7 +260,7 @@ subroutine read_model_atom(atomunit, atom) endif - !Van der Waaks collision method + !Van der Waals collision method !line%a allocated in opacity_atom.f90 atom%lines(kr)%cvdWaals(4) = 0. atom%lines(kr)%cvdWaals(2) = 0. @@ -281,8 +283,6 @@ subroutine read_model_atom(atomunit, atom) case default atom%lines(kr)%vdWaals = "UNSOLD" end select - !-> move to make_wavelengths_nlte in wavlengths_gas - ! call compute_line_bound(atom%lines(kr),.false.) end do !end loop over bound-bound transitions ! ----------------------------------------- ! @@ -361,7 +361,7 @@ subroutine read_model_atom(atomunit, atom) call search_cont_lambdamax (atom%continua(kr), atom%Rydberg, atom%stage(i)+1,atom%E(j),atom%E(i)) endif endif - !the Nlambda from file is not used except if not hydrogenic + !Not used except if not hydrogenic atom%continua(kr)%Nlambda = 0 case default @@ -372,12 +372,15 @@ subroutine read_model_atom(atomunit, atom) atom%set_ltepops = .true. !by default compute lte populations !non-LTE pops in electronic density ? write non-LTE pops to file ? - atom%NLTEpops = .false. !set to true during non-LTE loop if initial /= 0 + atom%NLTEpops = .false. ! set to true during non-LTE loop (initial /= 1). + ! true if read from file (initial==1). ! allocate some space - if (atom%initial==2) then + if (atom%initial>1) then if (.not.atom%active) then - write(*,*) atom%ID, " is passive! cannot use ZERO_RADIATION solution, set to LTE." + write(*,*) atom%ID, " is passive!" + write(*,*) " *** Only LTE_POPS or OLD_POPS are possible as initial conditions" + write(*,*) " *** setting to LTE" atom%initial=0 endif endif @@ -388,81 +391,66 @@ subroutine read_model_atom(atomunit, atom) ! reading collision rates of RH, even for H even if it is not used. if (atom%ID /= "X") then - write(*,*) " -> Reading collision data from RH for atom ", atom%ID + write(*,*) " *** Reading collision data from RH for atom ", atom%ID call read_collisions(atomunit, atom) call warning(" ** the data for each transition must be consistent with transitions in the file") write(*,*) "a value of -99 is given to missing transitions and are skipped **" endif + !if the atom is active, we must allocate the non-LTE populations arrays. allocate(atom%n(atom%Nlevel,n_cells)) atom%n = 0.0_dp - if (atom%initial == 0) then!(initial==0 .or. initial==3) - atom%n = atom%nstar !still need to be computed - atom%set_ltepops = .true. - !if (initial==0) then - write(*,*) " -> Setting initial solution to LTE " - !else - ! cswitch - !endif - - - else if (atom%initial == 1) then - - write(*,*) " -> Reading (non-LTE AND LTE) populations from file..." - call read_pops_atom(atom) - atom%NLTEpops = .true. - atom%set_ltepops = .false. !read and USE also LTE populations from file!! - - else if (atom%initial == 2) then - - call error("initial solution == 2 (opt thin) not implemented yet!") - atom%n = atom%nstar - atom%set_ltepops = .true. - !nlte pops is false and are set after first electron density and lte pops. - - else if (atom%initial == 3) then - atom%n = atom%nstar !still need to be computed - !like initial==0 - atom%set_ltepops = .true. - if (.not. lforce_lte) then !otherwise cswitch is not LTE - write(*,*) " -> Setting initial solution to LTE with CSWITCH " - atom%cswitch = cswitch_val - if (.not. lcswitch_enabled) lcswitch_enabled = .true.!we need at least one - endif - - else if (atom%initial == 4) then - - call error("initial solution == 4 (Sobolev) not implemented yet!") - atom%n = atom%nstar - atom%set_ltepops = .true. - !nlte pops is false and are set after first electron density and lte pops. - - end if + select case (atom%initial) + case (0) + write(*,*) " -> Setting initial solution to LTE " + case (1) + write(*,*) " -> Setting initial solution to OLD_POPS " ! non-LTE + LTE + call read_pops_atom(atom) + atom%NLTEpops = .true. !read from file. + atom%set_ltepops = .false. !read from file, no need to compute. + case (2) + call error("initial solution == 2 (opt thin) not implemented yet!") + atom%set_ltepops = .true. + case (3) + atom%set_ltepops = .true. + if (.not. lforce_lte) then !otherwise cswitch is not LTE + write(*,*) " -> Setting initial solution to LTE with CSWITCH " + atom%cswitch = cswitch_val + if (.not. lcswitch_enabled) lcswitch_enabled = .true. !we need at least one + endif + case (4) + call error("initial solution == 4 (ESCAPE/LVG/Sobolev) not implemented yet!") + atom%set_ltepops = .true. + case default + write(*,*) "Active atom: initial = ", atom%initial + call error("Initial solution unknown!") + end select else !not active = PASSIVE - !other initials do not matter, always LTE ! (except if read) - if (atom%initial == 1) then - - allocate(atom%n(atom%Nlevel,n_cells)) !not allocated if passive, n->nstar - write(*,*) " -> Reading (non-LTE AND LTE) populations from file for passive atom..." - call read_pops_atom(atom) - !by default. Need a case with only LTE ? - atom%NLTEpops = .true. - atom%set_ltepops = .false. !read and USE also LTE populations from file!! - - !atom%NLTEpops = .false. still false at this point as we need pops to do electron densities - else !pure passive without nlte pops from previous run - ! atom%NLTEpops=.false. !-> default values - ! atom%set_ltepops = .true. - atom%n => atom%nstar !initialised to zero - !atom%n is an alias for nstar in this case - end if + ! Only intial = 0 (LTE) or 1 (from file) ! + select case (atom%initial) + case (0) !pure passive without nlte pops from previous run. + ! atom%NLTEpops = .false. !-> default values + ! atom%set_ltepops = .true. !-> default values + !%n is an alias for %nstar in that case. No need to allocate. + atom%n => atom%nstar ! initialised to nstar values + case (1) !non-LTE mode using previous populations. + ! Compute images and so on without updating non-LTE pops. + allocate(atom%n(atom%Nlevel,n_cells)) + write(*,*) " -> Reading (non-LTE AND LTE) populations from file." + call read_pops_atom(atom) + atom%NLTEpops = .true. !read from file. + atom%set_ltepops = .false. !read from file, no need to compute. + case default + write(*,*) "Passive atom: initial = ", atom%initial + call error("Initial solution unknown!") + end select end if !end is active deallocate(levelNumber) deallocate(determined) deallocate(parse_labs) !close atomic file - close(unit=atomunit) !later it will be open again for + close(unit=atomunit) return end subroutine read_Model_Atom @@ -807,6 +795,8 @@ subroutine read_pops_atom(atom) logical :: extend, simple, anynull, show_warning = .true. integer :: nelements, naxis2(4), Nl, naxis_found, hdutype, l, icell character(len=256) :: some_comments, popsF + integer :: i, k + real(kind=dp) :: ntotal status = 0 call ftgiou(unit,status) @@ -981,6 +971,21 @@ subroutine read_pops_atom(atom) endif + !Set ni/nj ratio for continuum transitions in case ne-> 0 and nj ->0 + !should be ok for lines (the ratio for lines is used only in the collision) + !TO DO: para + do k=1, n_cells + ntotal = sum(atom%nstar(:,k)) ! nHtot(k) * atom%Abund + do i=1, atom%Ncont + if (atom%nstar(atom%continua(i)%j,k)>1d-15*ntotal) then + atom%ni_on_nj_star(atom%continua(i)%i,k) = atom%nstar(atom%continua(i)%i,k)/atom%nstar(atom%continua(i)%j,k) + else + atom%ni_on_nj_star(atom%continua(i)%i,k) = 0.0 + endif + enddo + enddo + + do l=1,atom%Nlevel write(*,"('Level #'(1I3))") l write(*,'(" -- min(n)="(1ES20.7E3)" m^-3; max(n)="(1ES20.7E3)" m^-3")') , & diff --git a/src/gas/lte.f90 b/src/gas/lte.f90 index 146073755..54dd8597e 100644 --- a/src/gas/lte.f90 +++ b/src/gas/lte.f90 @@ -16,6 +16,7 @@ module lte implicit none integer, dimension(101) :: ndebye + real(kind=dp), parameter :: small_lte_fraction = 1d-15 contains @@ -106,7 +107,7 @@ subroutine LTEpops_H_loc (k) ! -------------------------------------------------------------- ! integer, intent(in) :: k logical :: locupa_prob - real(kind=dp) :: dEion, dE, sum, c2, phik, phiHmin + real(kind=dp) :: dEion, dE, sum, c2, phik, phiHmin, ntotal real(kind=dp) :: n_eff, wocc, chi0, wocc0, E00, E, Egs integer :: Z, dZ, i, m @@ -117,6 +118,7 @@ subroutine LTEpops_H_loc (k) sum = 1.0 phik = ne(k)*phi_jl(T(k),1.d0,1.d0,0.d0) !a constant of the temperature and electron density + ntotal = hydrogen%Abund*nHtot(k) do i=2, hydrogen%Nlevel @@ -141,35 +143,29 @@ subroutine LTEpops_H_loc (k) endif end do + !handle very low populations + mass conservation + if (hydrogen%nstar(i,k) < small_lte_fraction) hydrogen%nstar(i,k) = 0.0 + sum = sum+hydrogen%nstar(i,k) end do - hydrogen%nstar(1,k) = hydrogen%Abund*nHtot(k)/sum + hydrogen%nstar(1,k) = ntotal/sum + !denorm the other levels + hydrogen%nstar(2:hydrogen%Nlevel,k) = hydrogen%nstar(2:hydrogen%Nlevel,k)*hydrogen%nstar(1,k) - !test positivity, can be 0 - if ((hydrogen%nstar(1,k) < 0)) then !<= tiny_dp) then + !test positivity. Must be > 0. + !sum is either 1 (all in ground state) or infinity (fully ionised). And ntotal here > 0. + if ((hydrogen%nstar(1,k) < 0)) then write(*,*) " ************************************* " - write(*,*) "Warning too small gs pop", hydrogen%ID, hydrogen%nstar(i,k) + write(*,*) "error negative gs pop", hydrogen%ID, hydrogen%nstar(i,k) write(*,*) "cell=",k, hydrogen%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k) write(*,*) " ************************************* " stop end if + !never set to 0 the ground-state population. - do i=2,hydrogen%Nlevel !debug - hydrogen%nstar(i,k) = hydrogen%nstar(i,k)*hydrogen%nstar(1,k) - - if (hydrogen%nstar(i,k) < 0) then !<= tiny_dp) then - write(*,*) " ************************************* " - write(*,*) "Warning population of hydrogen ", hydrogen%ID, "lvl=", i, "nstar=",hydrogen%nstar(i,k), " lower than", & - " tiny_dp." - write(*,*) "cell=",k, hydrogen%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k), & - " n0=", hydrogen%nstar(1,k) - write(*,*) " ************************************* " - stop - end if - end do - - if (maxval(hydrogen%nstar(:,k)) >= huge_dp) then + !sanity check + if (maxval(hydrogen%nstar(:,k)) > huge_dp) then write(*,*) " ************************************* " write(*,*) "ERROR, populations of hydrogen larger than huge_dp" write(*,*) "cell=",k, hydrogen%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k) @@ -189,6 +185,16 @@ subroutine LTEpops_H_loc (k) hydrogen%nstar(1,k) = hydrogen%nstar(1,k) * wocc endif + !Set ni/nj ratio for continuum transitions in case ne-> 0 and nj ->0 + !should be ok for lines (the ratio for lines is used only in the collision) + do i=1, hydrogen%Ncont + if (hydrogen%nstar(hydrogen%continua(i)%j,k)>small_lte_fraction*ntotal) then + hydrogen%ni_on_nj_star(hydrogen%continua(i)%i,k) = & + hydrogen%nstar(hydrogen%continua(i)%i,k)/hydrogen%nstar(hydrogen%continua(i)%j,k) + else + hydrogen%ni_on_nj_star(hydrogen%continua(i)%i,k) = 0.0 + endif + enddo return end subroutine LTEpops_H_loc @@ -205,7 +211,7 @@ subroutine LTEpops_atom_loc(k,atom,debye) logical, intent(in) :: debye logical :: locupa_prob, print_diff real(kind=dp) :: dEion, dE, sum, c2, phik, phiHmin - real(kind=dp) :: n_eff, wocc, chi0, wocc0 + real(kind=dp) :: n_eff, wocc, chi0, wocc0, ntotal integer :: Z, dZ, i, m ! debye shielding activated: @@ -236,6 +242,7 @@ subroutine LTEpops_atom_loc(k,atom,debye) sum = 1.0 phik = ne(k)*phi_jl(T(k),1.d0,1.d0,0.d0) !a constant of the temperature and electron density + ntotal = atom%Abund*nHtot(k) do i=2, atom%Nlevel @@ -277,6 +284,7 @@ subroutine LTEpops_atom_loc(k,atom,debye) atom%nstar(i,k) = 0d0 endif end do + if (atom%nstar(i,k) < small_lte_fraction) atom%nstar(i,k) = 0.0 sum = sum+atom%nstar(i,k) !compute total pop = Sum_jSum_i nij !with Sum_i nij = Nj @@ -291,31 +299,21 @@ subroutine LTEpops_atom_loc(k,atom,debye) ! write(*,*) "-------------------" ! write(*,*) "Atom=",atom%ID, " A=", atom%Abund ! write(*,*) "ntot", ntotal_atom(k,atom), " nHtot=",nHtot(k) - atom%nstar(1,k) = atom%Abund*nHtot(k)/sum + atom%nstar(1,k) = ntotal/sum + !denorm the other levels + atom%nstar(2:atom%Nlevel,k) = atom%nstar(2:atom%Nlevel,k)*atom%nstar(1,k) - !test positivity, can be 0 - if (atom%nstar(1,k) < 0) then !<= tiny_dp) then + !test positivity. Must be > 0 + if (atom%nstar(1,k) < 0) then write(*,*) " ************************************* " - write(*,*) "Warning too small ground state population ", atom%ID, "n0=", atom%nstar(1,k) + write(*,*) "Error negative ground state population ", atom%ID, "n0=", atom%nstar(1,k) write(*,*) "cell=",k, atom%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k) - !atom%nstar(1,k) = tiny_dp write(*,*) " ************************************* " - stop !only if we test >= 0 + stop end if - do i=2,atom%Nlevel !debug - atom%nstar(i,k) = atom%nstar(i,k)*atom%nstar(1,k) - if (atom%nstar(i,k) < 0) then !<= tiny_dp) then - write(*,*) " ************************************* " - write(*,*) "Warning population of atom ", atom%ID, "lvl=", i, "nstar=",atom%nstar(i,k), " lower than", & - " tiny_dp."! Replacing by tiny_dp" - write(*,*) "cell=",k, atom%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k), & - " n0=", atom%nstar(1,k) - write(*,*) " ************************************* " - stop - end if - end do + !never set to 0 the ground-state population. - if (maxval(atom%nstar(:,k)) >= huge_dp) then + if (maxval(atom%nstar(:,k)) > huge_dp) then write(*,*) " ************************************* " write(*,*) "ERROR, populations of atom larger than huge_dp" write(*,*) "cell=",k, atom%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k) @@ -337,6 +335,16 @@ subroutine LTEpops_atom_loc(k,atom,debye) enddo endif + !Set ni/nj ratio for continuum transitions in case ne-> 0 and nj ->0 + !should be ok for lines (the ratio for lines is used only in the collision) + do i=1, atom%Ncont + if (atom%nstar(atom%continua(i)%j,k)>small_lte_fraction*ntotal) then + atom%ni_on_nj_star(atom%continua(i)%i,k) = atom%nstar(atom%continua(i)%i,k)/atom%nstar(atom%continua(i)%j,k) + else + atom%ni_on_nj_star(atom%continua(i)%i,k) = 0.0 + endif + enddo + return end subroutine LTEpops_atom_loc @@ -400,6 +408,8 @@ subroutine ltepops_atoms return end subroutine ltepops_atoms +!-> TO DO: need to handle very low populations + atom%ni_on_nj_star +!-> not used subroutine LTEpops_H() ! -------------------------------------------------------------- ! ! computed wrt the ground state of H I @@ -478,7 +488,7 @@ subroutine LTEpops_H() hydrogen%nstar(1,k) = hydrogen%Abund*nHtot(k)/sum !test positivity, can be 0 - if ((hydrogen%nstar(1,k) < 0)) then !<= tiny_dp) then + if ((hydrogen%nstar(1,k) < 0.0)) then write(*,*) " ************************************* " write(*,*) "Warning too small gs pop", hydrogen%ID, hydrogen%nstar(i,k) write(*,*) "cell=",k, hydrogen%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k) @@ -489,7 +499,7 @@ subroutine LTEpops_H() do i=2,hydrogen%Nlevel !debug hydrogen%nstar(i,k) = hydrogen%nstar(i,k)*hydrogen%nstar(1,k) - if (hydrogen%nstar(i,k) < 0) then !<= tiny_dp) then + if (hydrogen%nstar(i,k) < 0.0) then write(*,*) " ************************************* " write(*,*) "Warning population of hydrogen ", hydrogen%ID, "lvl=", i, "nstar=",hydrogen%nstar(i,k), & " lower than tiny_dp." @@ -500,7 +510,7 @@ subroutine LTEpops_H() end if end do - if (maxval(hydrogen%nstar(:,k)) >= huge_dp) then + if (maxval(hydrogen%nstar(:,k)) > huge_dp) then write(*,*) " ************************************* " write(*,*) "ERROR, populations of hydrogen larger than huge_dp" write(*,*) "cell=",k, hydrogen%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k) @@ -553,6 +563,8 @@ subroutine LTEpops_H() return end subroutine LTEpops_H +!-> TO DO: need to handle very low populations + atom%ni_on_nj_star +!-> not used subroutine LTEpops_atom(atom, debye) ! -------------------------------------------------------------- ! ! Computes LTE populations of each level of the atom. @@ -674,7 +686,7 @@ subroutine LTEpops_atom(atom, debye) atom%nstar(1,k) = atom%Abund*nHtot(k)/sum !test positivity, can be 0 - if (atom%nstar(1,k) < 0) then !<= tiny_dp) then + if (atom%nstar(1,k) < 0.0) then write(*,*) " ************************************* " write(*,*) "Warning too small ground state population ", atom%ID, "n0=", atom%nstar(1,k) write(*,*) "cell=",k, atom%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k) @@ -684,7 +696,7 @@ subroutine LTEpops_atom(atom, debye) end if do i=2,atom%Nlevel !debug atom%nstar(i,k) = atom%nstar(i,k)*atom%nstar(1,k) - if (atom%nstar(i,k) < 0) then !<= tiny_dp) then + if (atom%nstar(i,k) < 0.0) then write(*,*) " ************************************* " write(*,*) "Warning population of atom ", atom%ID, "lvl=", i, "nstar=",atom%nstar(i,k), " lower than", & " tiny_dp."! Replacing by tiny_dp" @@ -695,7 +707,7 @@ subroutine LTEpops_atom(atom, debye) end if end do - if (maxval(atom%nstar(:,k)) >= huge_dp) then + if (maxval(atom%nstar(:,k)) > huge_dp) then write(*,*) " ************************************* " write(*,*) "ERROR, populations of atom larger than huge_dp" write(*,*) "cell=",k, atom%ID, "dark?=",icompute_atomRT(k), "T=",T(k), "nH=",nHtot(k), "ne=",ne(k) @@ -740,6 +752,8 @@ subroutine LTEpops_atom(atom, debye) return end subroutine LTEpops_atom +!-> TO DO: need to handle very low populations + atom%ni_on_nj_star +!-> not used subroutine ltepops_atoms_1 () ! -------------------------------------------------------------- ! ! Computes LTE populations for each atom diff --git a/src/gas/opacity_atom.f90 b/src/gas/opacity_atom.f90 index f0db9b5ba..69a004877 100644 --- a/src/gas/opacity_atom.f90 +++ b/src/gas/opacity_atom.f90 @@ -7,6 +7,7 @@ module Opacity_atom use broad, Only : line_damping use voigts, only : voigt use occupation_probability, only : f_dissolve + use elements_type, only : phi_T use gas_contopac, only : H_bf_Xsection, alloc_gas_contopac, background_continua_lambda, & dealloc_gas_contopac, hnu_k use wavelengths, only : n_lambda @@ -420,8 +421,9 @@ subroutine opacity_atom_bf_loc(icell,N,lambda,chi,Snu) Nred = atom%continua(kr)%Nrc; Nblue = atom%continua(kr)%Nbc i = atom%continua(kr)%i; j = atom%continua(kr)%j - !ni_on_nj_star = ne(icell) * phi_T(icell, at%g(i)/at%g(j), at%E(j)-at%E(i)) - ni_on_nj_star = atom%nstar(i,icell)/(atom%nstar(j,icell) + 1d-100) + ! ni_on_nj_star = ne(icell) * phi_T(T(icell), atom%g(i), atom%g(j), atom%E(j)-atom%E(i)) + ! ni_on_nj_star = atom%nstar(i,icell)/atom%nstar(j,icell) + ni_on_nj_star = atom%ni_on_nj_star(i,icell) gij = ni_on_nj_star * exp(-hc_k/T(icell)/atom%continua(kr)%lambda0) if ((atom%n(i,icell) - atom%n(j,icell) * gij) <= 0.0_dp) then @@ -656,8 +658,9 @@ subroutine cross_coupling_cont(id,icell,at) Nb = at%continua(kr)%Nbc; Nr = at%continua(kr)%Nrc Nl = Nr-Nb+1 - !ni_on_nj_star = ne(icell) * phi_T(icell, at%g(i)/at%g(j), at%E(j)-at%E(i)) - ni_on_nj_star = at%nstar(i,icell)/(at%nstar(j,icell) + 1d-100) + ! ni_on_nj_star = ne(icell) * phi_T(T(icell), at%g(i), at%g(j), at%E(j)-at%E(i)) + ! ni_on_nj_star = at%nstar(i,icell)/at%nstar(j,icell) + ni_on_nj_star = at%ni_on_nj_star(i,icell) gij_0 = ni_on_nj_star * exp(-hc_k/T(icell)/at%continua(kr)%lambda0) @@ -720,8 +723,9 @@ subroutine cross_coupling_cont_i(id,icell,at) Nl = Nr-Nb+1 N1 = at%continua(kr)%Nb; N2 = at%continua(kr)%Nr - !ni_on_nj_star = ne(icell) * phi_T(icell, at%g(i)/at%g(j), at%E(j)-at%E(i)) - ni_on_nj_star = at%nstar(i,icell)/(at%nstar(j,icell) + 1d-100) + ! ni_on_nj_star = ne(icell) * phi_T(T(icell), atom%g(i), atom%g(j), atom%E(j)-atom%E(i)) + ! ni_on_nj_star = at%nstar(i,icell)/at%nstar(j,icell) + ni_on_nj_star = at%ni_on_nj_star(i,icell) gij = ni_on_nj_star * exp(-hc_k/T(icell)/at%continua(kr)%lambda0) diff --git a/src/gas/see.f90 b/src/gas/see.f90 index 8a7defd2b..bcfdb8990 100644 --- a/src/gas/see.f90 +++ b/src/gas/see.f90 @@ -8,7 +8,8 @@ module see use wavelengths, only : n_lambda use wavelengths_gas, only : Nlambda_max_line, Nlambda_max_trans, Nlambda_max_cont, n_lambda_cont, & tab_lambda_cont, tab_lambda_nm - use utils, only : gaussslv, solve_lin, is_nan_infinity_vector, linear_1D_sorted, is_nan_infinity_matrix + use utils, only : gaussslv, solve_lin, is_nan_infinity_vector, linear_1D_sorted, is_nan_infinity_matrix, & + matdiag, jacobi_sparse use opacity_atom, only : phi_loc, psi, chi_up, chi_down, uji_down, Itot, eta_atoms, xcoupling_cont, cross_coupling_cont_i use messages, only : warning, error use collision_atom, only : collision_rates_atom_loc, collision_rates_hydrogen_loc @@ -17,7 +18,7 @@ module see implicit none !populations below that threshold not taken into account in convergence. - real(kind=dp), parameter :: frac_limit_pops = 1d-15!1d-50 + real(kind=dp), parameter :: small_nlte_fraction = 1d-15!1d-50 !Variables for Non-LTE loop and MALI method real(kind=dp), allocatable :: ngpop(:,:,:,:) real(kind=dp), allocatable :: tab_Aji_cont(:,:,:), tab_Vij_cont(:,:,:) @@ -357,7 +358,7 @@ subroutine see_atom(id,icell,atom,dM) do l=1,atom%Nlevel atom%n(l,icell) = atom%n(l,icell) * ntotal - if (atom%n(l,icell) < frac_limit_pops * ntotal) then + if (atom%n(l,icell) < small_nlte_fraction * ntotal) then Nsmall_pops = Nsmall_pops + 1 level_index(lp) = l lp = lp + 1 @@ -504,8 +505,11 @@ subroutine init_radrates_atom(id,icell,atom) atom%continua(kr)%Rij(id) = 0.0_dp !updated value of ni and nj! !-> 0 if cont does not contribute to opac. + ! atom%continua(kr)%Rji(id) = nlte_fact * tab_Aji_cont(kr,atom%activeindex,icell) * & + ! atom%nstar(i,icell)/atom%nstar(j,icell) atom%continua(kr)%Rji(id) = nlte_fact * tab_Aji_cont(kr,atom%activeindex,icell) * & - atom%nstar(i,icell)/atom%nstar(j,icell) + atom%ni_on_nj_star(i,icell) + !check ratio ni_on_nj_star for the continua when there are multiple ionisation states (works for H, test for He) enddo do kr=1,atom%Nline @@ -567,7 +571,8 @@ subroutine accumulate_radrates_mali(id, icell, iray, domega) wphi = 0.0 !(Psi - Psi^\ast) eta - Ieff(1:Nl) = Itot(Nb:Nr,iray,id) - Psi(Nb:Nr,1,id) * eta_atoms(Nb:Nr,nact,id) + !-> cannot be negative. At worst, it is 0 if the emission is entirely local + Ieff(1:Nl) = max(Itot(Nb:Nr,iray,id) - Psi(Nb:Nr,1,id) * eta_atoms(Nb:Nr,nact,id),0.0_dp) ! do l=2, nl ! wl = c_light * (tab_lambda_nm(i0+l) - tab_lambda_nm(i0+l-1))/atom%lines(kr)%lambda0 @@ -633,8 +638,9 @@ subroutine accumulate_radrates_mali(id, icell, iray, domega) i = atom%continua(kr)%i; j = atom%continua(kr)%j - !ni_on_nj_star = ne(icell) * phi_T(icell, aatom%g(i)/aatom%g(j), aatom%E(j)-aatom%E(i)) - ni_on_nj_star = atom%nstar(i,icell)/atom%nstar(j,icell) + ! ni_on_nj_star = ne(icell) * phi_T(T(icell), atom%g(i), atom%g(j), atom%E(j)-atom%E(i)) + ! ni_on_nj_star = atom%nstar(i,icell)/atom%nstar(j,icell) + ni_on_nj_star = atom%ni_on_nj_star(i,icell) gij = ni_on_nj_star * exp(-hc_k/T(icell)/atom%continua(kr)%lambda0) if ((atom%n(i,icell) - atom%n(j,icell) * gij) <= 0.0_dp) cycle cont_loop @@ -646,7 +652,7 @@ subroutine accumulate_radrates_mali(id, icell, iray, domega) Jbar_up = 0.0 xcc_down = 0.0 - Ieff(1:Nl) = Itot(Nb:Nr,iray,id) - Psi(Nb:Nr,1,id) * eta_atoms(Nb:Nr,nact,id) + Ieff(1:Nl) = max(Itot(Nb:Nr,iray,id) - Psi(Nb:Nr,1,id) * eta_atoms(Nb:Nr,nact,id),0.0_dp) ! write(*,*) Itot(Nb:Nr,iray,id) ! write(*,*) Psi(Nb:Nr,1,id)*eta_atoms(Nb:Nr,nact,id) ! write(*,*) Psi(Nb:Nr,1,id) @@ -960,7 +966,7 @@ subroutine see_atoms_ne(id,icell,dM,dne) do j=1,at%Nlevel at%n(j,icell) = at%n(j,icell) * ( 1.0_dp + fvar((i-1)+j,id)/(1.0_dp + d_damp * abs(fvar((i-1)+j,id))) ) if (at%n(j,icell) < 0.0) neg_pops = .true. - ! if (at%n(j,icell) < frac_limit_pops * at%Abund*nHtot(icell) )then + ! if (at%n(j,icell) < small_nlte_fraction * at%Abund*nHtot(icell) )then ! write(*,*) "small pops for level ", j ! endif enddo @@ -972,9 +978,9 @@ subroutine see_atoms_ne(id,icell,dM,dne) if (verbose)write(*,*) ( 1.0 + fvar(neq_ne,id)/(1.0_dp + d_damp * abs(fvar(neq_ne,id))) ) if (verbose)write(*,*) fvar(neq_ne,id), d_damp ! 1d-16 - ! if (ne(icell) < frac_limit_pops * nhtot(icell)) write(*,*) "** small ne at cell ", icell - ! if ( (ne(icell) < frac_limit_pops * nHtot(icell)).or.(neg_pops) ) rest_damping = .true. - if ( (ne(icell) < frac_limit_pops * sum(pops_ion(:,:,id))).or.(neg_pops) ) rest_damping = .true. + ! if (ne(icell) < small_nlte_fraction * nhtot(icell)) write(*,*) "** small ne at cell ", icell + ! if ( (ne(icell) < small_nlte_fraction * nHtot(icell)).or.(neg_pops) ) rest_damping = .true. + if ( (ne(icell) < small_nlte_fraction * sum(pops_ion(:,:,id))).or.(neg_pops) ) rest_damping = .true. !-> here pops_ion has the old values ! !restart with more iterations and larger damping (more stable, slower convergence) if (rest_damping .and. d_damp < (damp_char + 1.0)) then @@ -1045,7 +1051,7 @@ subroutine see_atoms_ne(id,icell,dM,dne) i = 1 do n=1,NactiveAtoms at => Activeatoms(n)%p - dM = max(dM,maxval(abs(1.0 - npop_dag(i:(i-1)+at%Nlevel,id)/at%n(:,icell)))) + dM = max(dM,maxval(abs(1.0 - npop_dag(i:(i-1)+at%Nlevel,id)/at%n(:,icell)),at%n(:,icell)>0)) ngpop(1:at%Nlevel,at%activeindex,icell,1) = at%n(:,icell) !reset at%n(:,icell) = npop_dag(i:(i-1)+at%Nlevel,id) !the first value before iterations @@ -1272,6 +1278,7 @@ subroutine multivariate_newton_raphson (neq, df, f, x) real(kind=dp), intent(in) :: x(neq) real(kind=dp), intent(inout) :: df(neq,neq), f(neq) integer :: ieq, jvar + real(kind=dp) :: xp(neq), diag(neq) ! real(kind=dp) :: Adag(neq,neq), bdag(neq), res(neq) do ieq=1, neq @@ -1281,12 +1288,24 @@ subroutine multivariate_newton_raphson (neq, df, f, x) enddo enddo + !check sparsity + diag(:) = abs(matdiag(df,neq)) + if (minval(diag)==0.0_dp) then + ! call warning("(Newton-Raphson) df is sparse!") + xp(:) = x(:) + call Jacobi_sparse(df,f,xp,neq) + f(:) = xp(:) + return + endif + ! *********************** ! ! Adag(:,:) = df(:,:) ! bdag(:) = f(:) ! *********************** ! ! call GaussSlv(df,f,neq) + ! write(*,*) "f=", f + ! write(*,*) "df=", df call solve_lin(df,f,neq) if (is_nan_infinity_vector(f)>0) then ! write(*,*) "fdag=", bdag diff --git a/src/gas/wavelengths_gas.f90 b/src/gas/wavelengths_gas.f90 index eed7d2a23..18075eea5 100644 --- a/src/gas/wavelengths_gas.f90 +++ b/src/gas/wavelengths_gas.f90 @@ -258,9 +258,13 @@ function line_lambda_grid_dv(line,Nlambda) return end function line_lambda_grid_dv - subroutine deallocate_wavelengths_gasrt(lambda) - real(kind=dp), intent(inout), allocatable, dimension(:) :: lambda - if (allocated(lambda)) deallocate(lambda) + subroutine deallocate_wavelengths_gasrt()!(lambda) + use wavelengths, only : n_lambda, tab_lambda, tab_lambda_inf, tab_lambda_sup, tab_delta_lambda, n_lambda2, tab_lambda2 + ! real(kind=dp), intent(inout), allocatable, dimension(:) :: lambda + if (allocated(tab_lambda)) deallocate(tab_lambda) !lambda + if (allocated(tab_lambda_inf)) then + deallocate(tab_lambda_inf, tab_lambda_sup, tab_delta_lambda) + endif if (allocated(tab_lambda_nm)) deallocate(tab_lambda_cont, tab_lambda_nm) !deallocate atom grid.. !deallocate group.. diff --git a/src/init_mcfost.f90 b/src/init_mcfost.f90 index 338e5ddb8..f5f2c7b00 100644 --- a/src/init_mcfost.f90 +++ b/src/init_mcfost.f90 @@ -82,6 +82,7 @@ subroutine set_default_variables() lsafe_stop = .false. safe_stop_time = 155520.0!1.8days in seconds, default lemission_atom = .false. + ldust_atom = .false. !coupling dust and atomic RT lelectron_scattering = .false. lstop_after_jnu = .false. lsolve_for_ne = .false. diff --git a/src/input.f90 b/src/input.f90 index 365b7e851..693240d23 100644 --- a/src/input.f90 +++ b/src/input.f90 @@ -181,9 +181,16 @@ subroutine lect_Temperature() integer :: status, readwrite, unit, blocksize,nfound,group,firstpix,nbuffer,npixels, hdutype real :: nullval integer, dimension(5) :: naxes - logical :: anynull - - if (lemission_atom) return !B. Tessore., Temporary + logical :: anynull, there_is_dust + + !future: lgas_transfer. Dust could not always be present in the gas RT. + if (lemission_atom) then + there_is_dust = (maxval(densite_pouss) > 0_dp) + if ( .not.there_is_dust ) then + call warning("(lect_Temperature) Do not attempt to read Tdust file when there is not dust!") + return + endif + endif if (lRE_LTE) then status=0 diff --git a/src/mem.f90 b/src/mem.f90 index 2593f612a..2b68c2e24 100644 --- a/src/mem.f90 +++ b/src/mem.f90 @@ -444,6 +444,83 @@ subroutine clean_mem_dust_mol() end subroutine clean_mem_dust_mol +subroutine realloc_dust_atom() +!this routine should be the mirror of the mol one, except for the tab_lambda which is allocated +!when the gas atom RT grid is defined (from reading the atomic species). +!Still, the test on the allocation and the call of clean_mem_dust_mol in init_dust_atom means +!that theya re not deallocated after temperature calculation. What am I missing ? + +! use stars, only : allocate_stellar_spectra !-> not use yet in atom transfer. + + + integer :: alloc_status + real :: mem_size + + if (lvariable_dust) then + write(*,*) " WARNING: sizes of dust transfer could be very big !" + !TO DO: better storing of quantities / recuction of n_lambda + endif + + !Note: tab_lambda(n_lambda) is already allocated in atomic_transfer + ! the tab_lambda_* or tab_delta_lambda should be de-allocated when tab_lambda is allocated in atom_rt. + allocate(tab_lambda_inf(n_lambda), tab_lambda_sup(n_lambda), tab_delta_lambda(n_lambda), & + tab_amu1(n_lambda, n_pop), tab_amu2(n_lambda, n_pop), & + tab_amu1_coating(n_lambda, n_pop), tab_amu2_coating(n_lambda, n_pop), stat=alloc_status) + if (alloc_status > 0) call error('Allocation error tab_lambda (realloc)') +! tab_lambda=0.0 + tab_lambda_inf = 0.0 ; tab_lambda_sup = 0.0 ; tab_delta_lambda= 0.0 ; + tab_amu1=0.0 ; tab_amu2=0.0 ; tab_amu1_coating=0.0 ; tab_amu2_coating=0.0 + + allocate(tab_albedo(n_grains_tot,n_lambda), stat=alloc_status) + if (alloc_status > 0) call error('Allocation error tab_albedo (realloc)') + tab_albedo = 0 + + allocate(C_ext(n_grains_tot,n_lambda), C_sca(n_grains_tot,n_lambda), & + C_abs(n_grains_tot,n_lambda), C_abs_norm(n_grains_tot,n_lambda), tab_g(n_grains_tot,n_lambda), stat=alloc_status) + if (alloc_status > 0) call error('Allocation error C_ext (realloc)') + C_ext = 0 ; C_sca = 0 ; C_abs = 0 ; C_abs_norm = 0 ; tab_g = 0 + + ! Tableaux relatifs aux prop optiques des cellules + if (allocated(kappa)) deallocate(kappa) + if (allocated(kappa_abs_LTE)) deallocate(kappa_abs_LTE) + if (allocated(kappa_factor)) deallocate(kappa_factor) + allocate(kappa(p_n_cells,n_lambda),kappa_abs_LTE(p_n_cells,n_lambda), kappa_factor(n_cells), stat=alloc_status) + if (alloc_status > 0) call error('Allocation error emissivite_dust (realloc atom)') + kappa = 0.0 ; kappa_abs_LTE = 0.0 + !mind the shape of the array compared to the others. + if (allocated(emissivite_dust)) deallocate(emissivite_dust) + allocate(emissivite_dust(n_lambda,n_cells),stat=alloc_status) + if (alloc_status > 0) call error('Allocation error emissivite_dust (realloc atom)') + if (lvariable_dust.or.(sizeof(emissivite_dust)/1024.**3 > 5)) then + write(*,*) " *** WARNING: using ", sizeof(emissivite_dust)/1024.**3, " GB for emissivite_dust" + endif + emissivite_dust = 0.0 + + if (lRE_nLTE) then + if (allocated(kappa_abs_nlte)) deallocate(kappa_abs_nlte) + allocate(kappa_abs_nLTE(p_n_cells,n_lambda), stat=alloc_status) + if (alloc_status > 0) call error('Allocation error kappa_abs_nLTE (realloc atom)') + kappa_abs_nLTE = 0.0 + endif + + allocate(tab_albedo_pos(p_n_cells,n_lambda),stat=alloc_status) + if (alloc_status > 0) call error('Allocation error tab_albedo_pos (realloc atom)') + tab_albedo_pos = 0 + + if (aniso_method==2) then + allocate(tab_g_pos(p_n_cells,n_lambda),stat=alloc_status) + if (alloc_status > 0) call error('Allocation error tab_g_pos (realloc atom)') + tab_g_pos = 0.0 + endif + +! call allocate_stellar_spectra(n_lambda) + + return + +end subroutine realloc_dust_atom + +!****************************************************************************** + !****************************************************************************** subroutine realloc_step2() diff --git a/src/optical_depth.f90 b/src/optical_depth.f90 index 1f3f8dccf..d7091df2d 100644 --- a/src/optical_depth.f90 +++ b/src/optical_depth.f90 @@ -1087,11 +1087,10 @@ end subroutine optical_length_tot_mol !******************************************************************** subroutine integ_ray_atom(id,icell_in,x,y,z,u,v,w,iray,labs,N,lambda) ! ------------------------------------------------------------------------------- ! - ! TO DO: merge integ_ray_atom + integ_ray_line + ! TO DO: merge integ_ray_atom + integ_ray_mol ! Zeeman ! scattering ! level dissolution - ! dust ! ------------------------------------------------------------------------------- ! integer, intent(in) :: id, icell_in, iray real(kind=dp), intent(in) :: u,v,w @@ -1101,7 +1100,9 @@ subroutine integ_ray_atom(id,icell_in,x,y,z,u,v,w,iray,labs,N,lambda) real(kind=dp), dimension(N), intent(in) :: lambda real(kind=dp) :: x0, y0, z0, x1, y1, z1, l, l_contrib, l_void_before, Q, P(4) real(kind=dp), dimension(N) :: Snu, tau, dtau, chi, coronal_irrad - integer :: nbr_cell, icell, next_cell, previous_cell, icell_star, i_star, la, icell_prev + integer, target :: icell + integer, pointer :: p_icell + integer :: nbr_cell, next_cell, previous_cell, icell_star, i_star, la, icell_prev logical :: lcellule_non_vide, lsubtract_avg, lintersect_stars x1=x;y1=y;z1=z @@ -1114,6 +1115,9 @@ subroutine integ_ray_atom(id,icell_in,x,y,z,u,v,w,iray,labs,N,lambda) Itot(:,iray,id) = 0.0_dp + p_icell => icell_ref + if (lvariable_dust) p_icell => icell + ! Will the ray intersect a star call intersect_stars(x,y,z, u,v,w, lintersect_stars, i_star, icell_star) ! Boucle infinie sur les cellules (we go over the grid.) @@ -1123,11 +1127,6 @@ subroutine integ_ray_atom(id,icell_in,x,y,z,u,v,w,iray,labs,N,lambda) x0=x1 ; y0=y1 ; z0=z1 lcellule_non_vide = (icell <= n_cells) - ! if (icell <= n_cells) then - ! lcellule_non_vide=.true. - ! else - ! lcellule_non_vide=.false. - ! endif ! Test sortie ! "The ray has reach the end of the grid" if (test_exit_grid(icell, x0, y0, z0)) return @@ -1139,23 +1138,16 @@ subroutine integ_ray_atom(id,icell_in,x,y,z,u,v,w,iray,labs,N,lambda) return end if endif - !With the Voronoi grid, somme cells can have a negative index - !therefore we need to test_exit_grid before using icompute_atom_rt - if (icell <= n_cells) then - lcellule_non_vide = (icompute_atomRT(icell) > 0) - if (icompute_atomRT(icell) < 0) then - if (icompute_atomRT(icell) == -1) then - !If the optically thick region (dark zone) has a temperature - !add a black body emission and leave. - if (T(icell) > 0.0_dp) Itot(:,iray,id) = Itot(:,iray,id) + & - exp(-tau) * Bpnu(N,lambda,T(icell)) - return - else - !Does not return but cell is empty (lcellule_non_vide is .false.) - coronal_irrad = linear_1D_sorted(atmos_1d%Ncorona,atmos_1d%x_coro(:), & + + !Special handling of coronal irradiation from "above". + !mainly for 1d stellar atmosphere + if (lcellule_non_vide) then + if (icompute_atomRT(icell) == -2) then + !Does not return but cell is empty (lcellule_non_vide is .false.) + coronal_irrad = linear_1D_sorted(atmos_1d%Ncorona,atmos_1d%x_coro(:), & atmos_1d%I_coro(:,1),N,lambda) - Itot(:,iray,id) = Itot(:,iray,id) + exp(-tau) * coronal_irrad - endif + Itot(:,iray,id) = Itot(:,iray,id) + exp(-tau) * coronal_irrad + lcellule_non_vide = .false. endif endif @@ -1168,12 +1160,22 @@ subroutine integ_ray_atom(id,icell_in,x,y,z,u,v,w,iray,labs,N,lambda) !count opacity only if the cell is filled, else go to next cell if (lcellule_non_vide) then lsubtract_avg = ((nbr_cell == 1).and.labs) + chi(:) = 1d-300; Snu(:) = 0.0_dp ! opacities in m^-1, l_contrib in au - - call contopac_atom_loc(icell, N, lambda, chi, Snu) - call opacity_atom_bb_loc(id,icell,iray,x0,y0,z0,x1,y1,z1,u,v,w,& - l_void_before,l_contrib,lsubtract_avg,N,lambda,chi,Snu) + if (icompute_atomRT(icell) > 0) then + !re-init chi + call contopac_atom_loc(icell, N, lambda, chi, Snu) + call opacity_atom_bb_loc(id,icell,iray,x0,y0,z0,x1,y1,z1,u,v,w,& + l_void_before,l_contrib,lsubtract_avg,N,lambda,chi,Snu) + endif + !TO DO: move to the total contopac & + ! do not initialize chi, Snu in background but update them instead. + if (ldust_atom) then + chi = chi + kappa_abs_LTE(p_icell,:) * kappa_factor(icell) * m_to_AU ! [m^-1] + ! Snu = Snu + kappa_abs_LTE(p_icell,:) * kappa_factor(icell) * m_to_AU * Bpnu(N,lambda,T(icell)) ! [W m^-3 Hz^-1 sr^-1] + Snu = Snu + emissivite_dust(:,icell) ! [W m^-3 Hz^-1 sr^-1] + endif dtau(:) = l_contrib * chi(:) * AU_to_m !au * m^-1 * au_to_m diff --git a/src/parameters.f90 b/src/parameters.f90 index 3a6bb054b..c2ae788f3 100644 --- a/src/parameters.f90 +++ b/src/parameters.f90 @@ -55,6 +55,7 @@ module parametres character(len=512) :: tab_wavelength !gas transfer here + logical :: ldust_gas ! Emission moleculaire logical :: lemission_mol, lpop, lprecise_pop, lmol_LTE, ldust_mol, lonly_top, lonly_bottom @@ -62,7 +63,7 @@ module parametres ! Atomic line radiative transfer logical :: lexit_after_nonlte_loop, lstop_after_jnu logical :: lemission_atom, lelectron_scattering, lforce_lte, & - ldissolve, loutput_rates, lzeeman_polarisation + ldissolve, loutput_rates, lzeeman_polarisation, ldust_atom integer :: N_rayons_mc, istep_start, istep_end !HEALpix diff --git a/src/read1d_models.f90 b/src/read1d_models.f90 index 455a6e0d1..b2821987f 100644 --- a/src/read1d_models.f90 +++ b/src/read1d_models.f90 @@ -14,6 +14,8 @@ module read1d_models use elements_type use grid, only : cell_map, vfield3d, alloc_atomrt_grid, nHtot, ne, v_char, lmagnetized, vturb, T, icompute_atomRT, & lcalc_ne, check_for_zero_electronic_density + use density, only : densite_pouss + use grains, only : M_grain implicit none @@ -226,10 +228,12 @@ end subroutine setup_model1d_to_mcfost subroutine print_info_model real(kind=dp) :: v_char + real(kind=dp) :: dust_dens_max, dust_dens_min, rho_d + integer :: icell v_char = sqrt( maxval(sum(vfield3d**2,dim=2)) ) - write(*,*) "Maximum/minimum velocities in the model (km/s):" + write(*,*) "Maximum/minimum velocities in the model [km s^-1]:" write(*,*) " Vfield(1) = ", & 1e-3 * maxval(abs(vfield3d(:,1))), 1d-3*minval(abs(vfield3d(:,1)),mask=icompute_atomRT>0) write(*,*) " Vfield(2) = ", & @@ -238,18 +242,32 @@ subroutine print_info_model 1d-3 * maxval(abs(vfield3d(:,3))), 1d-3*minval(abs(vfield3d(:,3)),mask=icompute_atomRT>0) - write(*,*) "Typical line extent due to V fields (km/s):" + write(*,*) "Typical line extent due to V fields [km s^-1]:" write(*,*) v_char/1d3 - write(*,*) "Maximum/minimum turbulent velocity (km/s):" + write(*,*) "Maximum/minimum turbulent velocity [km s^-1]:" write(*,*) maxval(vturb)/1d3, minval(vturb, mask=icompute_atomRT>0)/1d3 - write(*,*) "Maximum/minimum Temperature in the model (K):" + write(*,*) "Maximum/minimum Temperature in the model [K]:" write(*,*) real(maxval(T)), real(minval(T,mask=icompute_atomRT>0)) - write(*,*) "Maximum/minimum Hydrogen total density in the model (m^-3):" + ! write(*,*) " --> Density average of the Temperature [K]:" + ! write(*,*) real(sum(T*nHtot,(sum(densite_pouss,dim=1)==0.0).and.(icompute_atomRT>0)) / & + ! sum(nHtot,(sum(densite_pouss,dim=1)==0.0).and.(icompute_atomRT>0))) + write(*,*) "Maximum/minimum Hydrogen total density in the model [m^-3]:" write(*,*) real(maxval(nHtot)), real(minval(nHtot,mask=icompute_atomRT>0)) + if (ldust_atom) then + dust_dens_max = 0d0; dust_dens_min = 1d30 + do icell=1, n_cells + rho_d = sum(densite_pouss(:,icell) * M_grain(:)) + if (rho_d<=0.0) cycle + dust_dens_min = min(dust_dens_min,rho_d) + dust_dens_max = max(dust_dens_max,rho_d) + enddo + write(*,*) "Maximum/minimum dust total density in the model [kg m^-3]:" + write(*,*) 1d3*dust_dens_max, 1d3*dust_dens_min + endif if (.not.lcalc_ne) then - write(*,*) "Maximum/minimum ne density in the model (m^-3):" + write(*,*) "Maximum/minimum ne density in the model [m^-3]:" write(*,*) real(maxval(ne)), real(minval(ne,mask=icompute_atomRT>0)) endif @@ -259,7 +277,7 @@ subroutine print_info_model endif write(*,*) "Read ", size(pack(icompute_atomRT,mask=icompute_atomRT>0)), " density zones" write(*,*) "Read ", size(pack(icompute_atomRT,mask=icompute_atomRT==0)), " transparent zones" - write(*,*) "Read ", size(pack(icompute_atomRT,mask=icompute_atomRT<0)), " dark zones" + ! write(*,*) "Read ", size(pack(icompute_atomRT,mask=icompute_atomRT<0)), " dark zones" write(*,'("-- Solving RTE for "(1F6.2)" % of cells")') & 100.0*real(size(pack(icompute_atomRT,mask=icompute_atomRT>0))) / real(n_cells) diff --git a/src/read_spherical_grid.f90 b/src/read_spherical_grid.f90 index 4e02d42eb..da98494d2 100644 --- a/src/read_spherical_grid.f90 +++ b/src/read_spherical_grid.f90 @@ -1,7 +1,5 @@ ! -! Read model generated on a structured spherical mesh (e.g. with numpy.meshgrid). -! The model is in binary format generated with scipy.io.FortranFile.write_record() -! +! Read model generated on a structured spherical mesh (e.g. with numpy.meshgrid).! ! module read_spherical_grid @@ -13,6 +11,8 @@ module read_spherical_grid use messages use utils use cylindrical_grid + use density + use dust_prop, only : dust_pop use stars, only : T_hp, T_preshock use read1d_models, only : print_info_model @@ -39,33 +39,36 @@ subroutine read_spherical_grid_parameters(filename) vfield_coord = 3 ! spherical grid_type = 2 n_rad_in = 1 + n_zones = 1 open(unit=1, file=trim(filename), status="old",access="stream",form='unformatted') !read size along each direction + cell limits (size + 1) read(1,iostat=ios) pluto%nx1 allocate(pluto%x1(pluto%nx1+1)) read(1,iostat=ios) pluto%x1(:) + !Rmin to Rmax pluto%x1_min = minval(pluto%x1); pluto%x1_max = maxval(pluto%x1) - write(*,*) "r_limits (read)=",pluto%x1(:)! / etoile(1)%r + ! write(*,*) "r_limits [Rstar(1)] (read)=", pluto%x1(:) / etoile(1)%r + write(*,*) "r_limits [Rstar(1)] (read)=", pluto%x1(1) / etoile(1)%r, pluto%x1(pluto%nx1+1) / etoile(1)%r read(1,iostat=ios) pluto%nx2 allocate(pluto%x2(pluto%nx2+1)) read(1,iostat=ios) pluto%x2(:) + !pi to 0 or pi/2 to 0 if 2d. pluto%x2_min = minval(pluto%x2); pluto%x2_max = maxval(pluto%x2) - write(*,*) "sin(theta)_limits (read)=", pluto%x2(:) - !correct prec error ? - ! if (pluto%x2(1)-0.5*pi < 1e-6) pluto%x2(1) = 0.5*pi - write(*,*) "theta_limits (read)=",180.0 * real(asin(real(pluto%x2(:),kind=dp))) / pi + ! write(*,*) "theta_limits [°] (read)=", pluto%x2(:) + write(*,*) "theta_limits [°] (read)=", pluto%x2(1), pluto%x2(pluto%nx2+1) read(1,iostat=ios) pluto%nx3 !special, only 1 azimuth if not 3D (' no limits ') Nsize = pluto%nx3 if (pluto%nx3 > 1) then - ! l3D = .true. Nsize = Nsize + 1 endif allocate(pluto%x3(Nsize)) read(1,iostat=ios) pluto%x3(:) + !0 to 2pi pluto%x3_min = minval(pluto%x3); pluto%x3_max = maxval(pluto%x3) - write(*,*) "phi_limits (read)=",180.0 * pluto%x3(:) / pi + ! write(*,*) "phi_limits [rad] (read)=", pluto%x3(:) + write(*,*) "phi_limits [°] (read)=", 180.0 * pluto%x3(1) / pi, 180.0 * pluto%x3(size(pluto%x3)) / pi read(1, iostat=ios) acc read(1, iostat=ios) T_hp @@ -76,10 +79,11 @@ subroutine read_spherical_grid_parameters(filename) if (pluto%x2(1) < pluto%x2(pluto%nx2)) then call error("(spherical input grid) theta(1) must be the largest value (pi or pi/2)") endif + !re-order pluto%x2 such that it goes from 0 to pi/2 from 1 to nz+1 + pluto%x2(:) = pluto%x2(pluto%nx2+1:1:-1) - !either 2.5d (min(sin(theta))=-1) or 3d (Nphi > 1) - l3d = (minval(pluto%x2) < 0.0).or.(pluto%nx3 > 1) - write(*,*) "3d mode ? ", l3d + ! 3d 2.5d + l3d = (pluto%nx3 > 1).or.(abs(maxval(pluto%x2) - 0.5 * pi) > 1e-6) laccretion_shock = (acc == 1) if (T_hp == 0.0_dp) T_hp = -1.0_dp @@ -88,60 +92,59 @@ subroutine read_spherical_grid_parameters(filename) n_rad = pluto%nx1 n_az = pluto%nx3 nz = pluto%nx2 - theta_max = pluto%x2_max + !but not used anyway + theta_max = 0.5_dp * pi ! should always be pi/2 (?) ! pluto%x2_max + !beware pluto%x1_min must not overlap with the core (star, planet etc). disk_zone(1)%rin = pluto%x1_min - disk_zone(1)%edge=0.0 + disk_zone(1)%edge = 0.0 disk_zone(1)%rmin = disk_zone(1)%rin disk_zone(1)%rout = pluto%x1_max - ! if (maxval(pluto%x1) == pluto%x1_max) then - ! pluto%x1_max = pluto%x1_max + pluto%x1(pluto%nx1) - pluto%x1(pluto%nx1-1) - ! endif disk_zone(1)%rmax = disk_zone(1)%rout - !change nz to two hemispheres and check that the grid of phi is linear. if (l3d) then + !handle the case 2.5d where Np=1 but %x2 goes to pi to 0. + !In those cases we have to give half the points in nz. nz = pluto%nx2/2 if (mod(pluto%nx2,2)>0) call warning("odd nx2") - ! dphi = pluto%x3(2) - pluto%x3(1) - ! do i=2, size(pluto%x3) - ! if ( abs((pluto%x3(i) - pluto%x3(i-1)) - dphi) > 1e-6 ) then - ! write(*,*) dphi, (pluto%x3(i) - pluto%x3(i-1)),abs((pluto%x3(i) - pluto%x3(i-1)) - dphi) - ! call error("(spherical input grid) Non-linear phi space is not allowed!") - ! endif - ! dphi = pluto%x3(i) - pluto%x3(i-1) - ! enddo + endif + + !test on nx3 in the envtuallity of 2.5d + if (pluto%nx3 > 1) then + !check phi grid is pluto%nx3 > 1 + dphi = pluto%x3(2) - pluto%x3(1) + do i=2, size(pluto%x3) + if ( abs((pluto%x3(i) - pluto%x3(i-1)) - dphi) > 1e-6 ) then + write(*,*) dphi, (pluto%x3(i) - pluto%x3(i-1)),abs((pluto%x3(i) - pluto%x3(i-1)) - dphi) + call error("(spherical input grid) Non-linear phi space is not allowed!") + endif + dphi = pluto%x3(i) - pluto%x3(i-1) + enddo endif Nsize = pluto%nx2 * pluto%nx3 * pluto%nx1 write(*,*) "Nsize=", Nsize, " nx1=", pluto%nx1, " nx2=", pluto%nx2," nx3=", pluto%nx3 write(*,*) "n_rad=", n_rad, "nz=", nz, "n_az=", n_az - write(*,*) "rin=", real(disk_zone(1)%rin), "rout=", real(disk_zone(1)%rout) + write(*,*) "rin=", real(disk_zone(1)%rin), "[au]; ", "rout=", real(disk_zone(1)%rout), "[au]" return endsubroutine read_spherical_grid_parameters - !building (cleaner version) - subroutine accomodate_spherical_grid() - !redifine the limits of the spherical grid - !to match the limits read with read_spherical_grid_parameters - - return - end subroutine accomodate_spherical_grid - - subroutine read_spherical_model(filename) ! ----------------------------------------------------- ! ! read spherical grid data defined at cell centres. ! ----------------------------------------------------- ! + ! use grains, only : M_Grain character(len=*), intent(in) :: filename integer :: ios, i, Nsize integer :: j, k, jj, icell integer, allocatable :: dz(:,:,:) real, allocatable :: vtmp(:,:,:,:) - real(kind=dp), allocatable :: rho(:,:,:), T_tmp(:,:,:), ne_tmp(:,:,:), vt_tmp(:,:,:) + real(kind=dp), allocatable :: rho(:,:,:), rho_dust(:,:,:) + real(kind=dp), allocatable :: T_tmp(:,:,:), ne_tmp(:,:,:), vt_tmp(:,:,:) + real(kind=dp) :: mass call alloc_atomrt_grid call read_abundance !can be move in atom_transfer, but then rho must be changed in nHtot @@ -152,17 +155,6 @@ subroutine read_spherical_model(filename) Nsize = pluto%nx1*pluto%nx2*pluto%nx3 write(*,*) "n_cells=", n_cells, Nsize - ! --> implicit cell mapping (does not work if the data is not well ordered) - ! read(1, iostat=ios) T(:) - ! read(1, iostat=ios) nHtot(:) - ! read(1, iostat=ios) ne(:) - ! read(1, iostat=ios) vfield3d(:,:) - ! read(1, iostat=ios) vturb(:) - ! read(1, iostat=ios) icompute_atomRT(:) - ! close(unit=1) - ! ! stop - ! !rho -> nH - ! nHtot = nHtot * 1d3 / masseH / wght_per_H ! --> explicit cell mapping of the 3d arrays allocate(rho(pluto%nx1,pluto%nx2,pluto%nx3), ne_tmp(pluto%nx1,pluto%nx2,pluto%nx3), & @@ -174,23 +166,104 @@ subroutine read_spherical_model(filename) read(1, iostat=ios) vtmp(:,:,:,:) read(1, iostat=ios) vt_tmp(:,:,:) read(1, iostat=ios) dz(:,:,:) + read(1, iostat=ios) disk_zone(1)%gas_to_dust + write(*,*) "Gas/Dust from model:", real(disk_zone(1)%gas_to_dust) + !read total dust density + allocate(rho_dust(pluto%nx1,pluto%nx2,pluto%nx3)) + read(1, iostat=ios) rho_dust(:,:,:) close(unit=1) + densite_pouss = 0.0_dp ! init just in case. + masse_gaz = 0.0_dp + disk_zone(1)%diskmass = 0.0 + ! do i=1, n_rad + ! j = 0 + ! bz : do jj=j_start+1,nz-1 ! 1 extra empty cell in theta on each side + ! if (jj==0) cycle bz + ! j = j + 1 + ! do k=1, n_az + ! icell = cell_map(i,jj,k) + ! T(icell) = T_tmp(i,j,k) + ! nHtot(icell) = rho(i,j,k) * 1d3 / masseH / wght_per_H ! [H/m^3] + ! icompute_atomRT(icell) = dz(i,j,k) + ! vfield3d(icell,:) = vtmp(i,j,k,:) + + ! !-> wrapper for dust RT. + ! !-> taking into account proper weights assuming only molecular gas in dusty regions + ! if (rho_dust(i,j,k) > 0.0) then ! dusty region + ! densite_gaz(icell) = rho(i,j,k) * 1d3 / masse_mol_gaz !total molecular gas density in H2/m^3 + ! densite_pouss(:,icell) = rho_dust(i,j,k) * 1d3 / masse_mol_gaz ! [m^-3] + ! disk_zone(1)%diskmass = disk_zone(1)%diskmass + rho_dust(i,j,k) * volume(icell) + ! else !No dust. + ! densite_gaz(icell) = nHtot(icell) * wght_per_H !total atomic gas density in [m^-3] + ! endif + ! masse_gaz(icell) = rho(i,j,k) * volume(icell) + ! enddo ! phi + ! enddo bz ! theta + ! enddo ! r do i=1, n_rad - j = 0 - bz : do jj=j_start+1,nz-1 ! 1 extra empty cell in theta on each side - if (jj==0) cycle bz - j = j + 1 + bz : do jj=min(0,j_start),nz do k=1, n_az - icell = cell_map(i,jj,k) + if (jj==0) then + icell = cell_map(i,1,k) + j = 1 + else + j = jj + if (l3d) then + if (jj > 0) then + j = jj + nz + else + j = nz + 1 + jj + endif + endif + icell = cell_map(i,jj,k) + endif T(icell) = T_tmp(i,j,k) - nHtot(icell) = rho(i,j,k) * 1d3 / masseH / wght_per_H + nHtot(icell) = rho(i,j,k) * 1d3 / masseH / wght_per_H ! [H/m^3] icompute_atomRT(icell) = dz(i,j,k) vfield3d(icell,:) = vtmp(i,j,k,:) - enddo - enddo bz - enddo + + !-> wrapper for dust RT. + !-> taking into account proper weights assuming only molecular gas in dusty regions + if (rho_dust(i,j,k) > 0.0) then ! dusty region + densite_gaz(icell) = rho(i,j,k) * 1d3 / masse_mol_gaz !total molecular gas density in H2/m^3 + densite_pouss(:,icell) = rho_dust(i,j,k) * 1d3 / masse_mol_gaz ! [m^-3] + disk_zone(1)%diskmass = disk_zone(1)%diskmass + rho_dust(i,j,k) * volume(icell) + else !No dust. + densite_gaz(icell) = nHtot(icell) * wght_per_H !total atomic gas density in [m^-3] + endif + masse_gaz(icell) = rho(i,j,k) * volume(icell) + enddo ! phi + enddo bz ! theta + enddo ! r + masse_gaz = masse_gaz * AU3_to_m3 * 1d3 ! [g] deallocate(rho,ne_tmp,T_tmp,vt_tmp,dz,vtmp) + !total dust mass + disk_zone(1)%diskmass = disk_zone(1)%diskmass * AU3_to_m3 * kg_to_Msun + if (allocated(rho_dust)) deallocate(rho_dust) + + !dust part see read_pluto.f90 + ! ********************************** ! + mass = sum(masse_gaz) * g_to_Msun + ! mass = sum(densite_gaz * volume) * AU3_to_m3 * masseH * g_to_Msun + if (mass <= 0.0) call error('Gas mass is 0') + ! masse_gaz(:) = masseH * densite_gaz(:) * volume(:) * AU3_to_m3 ! [g] + + !--> no normalisation of density. The dust and gas densities are provided in the model. + + write(*,*) 'Total gas mass in model:', real(sum(masse_gaz) * g_to_Msun),' Msun' + ! write(*,*) 'Total dust mass in model:', real(disk_zone(1)%diskmass),' Msun' + if (disk_zone(1)%diskmass > 0.0_dp) then + dust_pop(:)%masse = disk_zone(1)%diskmass + !here the densite_pouss gets normalised such that sum(densite_pouss) = 1 [units less] + call normalize_dust_density() + !the units of densite_pouss comes from M_grain in g/cm^3, normalize to give the dust mass if summed. + !the density of grains is M_grain x densite_pouss + if (lemission_atom) ldust_atom = .true. + endif + ! ********************************** ! + ! write(*,*) "icell_not_empty:", icell_not_empty + ! write(*,*) "rho(icell_not_empty)", maxval(densite_pouss(:,icell_not_empty)), densite_gaz(icell_not_empty) call check_for_zero_electronic_density() call print_info_model() diff --git a/src/stars.f90 b/src/stars.f90 index 1879e4282..54842788a 100644 --- a/src/stars.f90 +++ b/src/stars.f90 @@ -6,7 +6,6 @@ module stars use messages use wavelengths use grid - use elements_type, only : wght_per_H implicit none @@ -949,10 +948,12 @@ function is_inshock(id, iray, i_star, icell0, x, y, z, Thp, Tshock, Facc) ! use grid, only : voronoi use constantes, only : sigma, kb + use elements_type, only : wght_per_H + ! use density, only : densite_gaz logical :: is_inshock integer :: i_star, icell0, id, iray real(kind=dp), intent(out) :: Thp, Tshock, Facc - real(kind=dp) :: x, y, z + real(kind=dp) :: x, y, z, rho real(kind=dp) :: Tloc, vaccr, vmod2, rr, sign_z real :: alpha_1 = 0.75 real :: alpha_2 = 0.25 @@ -961,10 +962,11 @@ function is_inshock(id, iray, i_star, icell0, x, y, z, Thp, Tshock, Facc) if (.not.laccretion_shock) return if (icell0<=n_cells) then - if (nHtot(icell0) > 0.0) then - ! if (icompute_atomRT(icell0) > 0) then + !TO DO: densite_gaz(icell0) instead of nHtot + rho = nHtot(icell0) * wght_per_H + if (rho > 0.0) then ! even if icompute_atomRT(icell0) /= 0 rr = sqrt( x*x + y*y + z*z) - !vaccr is vr, the spherical r velocity component + ! Get vaccr : the accretion velocity above the shock. if (lvoronoi) then !always 3d vaccr = Voronoi(icell0)%vxyz(1)*x/rr + Voronoi(icell0)%vxyz(2)*y/rr + Voronoi(icell0)%vxyz(3) * z/rr vmod2 = sum( Voronoi(icell0)%vxyz(:)**2 ) @@ -992,13 +994,14 @@ function is_inshock(id, iray, i_star, icell0, x, y, z, Thp, Tshock, Facc) if (vaccr < 0.0_dp) then !Facc = 1/2 rho vs^3 - Facc = 0.5 * (1d-3 * masseH * wght_per_H * nHtot(icell0)) * abs(vaccr)**3 + Facc = 0.5 * (1d-3 * masseH * rho) * abs(vaccr)**3 Tloc = ( alpha_1 * Facc / sigma )**0.25 ! is_inshock = (Tloc > 0.5 * etoile(i_star)%T) is_inshock = (T_hp > 1.0_dp * etoile(i_star)%T) Thp = T_hp if (T_hp<=0.0) then - is_inshock = (abs(T_hp) * Tloc > 1.0_dp*etoile(i_star)%T) !depends on the local value + !depends on the local value + is_inshock = (abs(T_hp) * Tloc > 1.0_dp*etoile(i_star)%T) Thp = abs(T_hp) * Tloc endif !assuming mu is 0.5 diff --git a/src/utils.f90 b/src/utils.f90 index f3c7511ef..dff0d5ec8 100644 --- a/src/utils.f90 +++ b/src/utils.f90 @@ -226,6 +226,102 @@ real(kind=dp) function interp_dp(y, x, xp) end function interp_dp !********************************************************************** +function matdiag(A,n) + !return the diagonal of a square matrix A(N,N) + !as a vector of length n. + integer, intent(in) :: n + real(kind=dp), intent(in) :: A(n*n) + ! real(kind=dp), intent(in) :: A(n,n) + real(kind=dp) :: matdiag(n) + integer :: i + + ! do i = 1, n + ! matdiag(i) = A(i,i) + ! end do + matdiag(:) = A(1::n+1) + + return +end function matdiag + +subroutine Jacobi(a,b,x,n) +!solve a system Ax=b with the iterative jacobi method + integer, intent(in) :: n + real(kind=dp) :: a(n,n), b(n) + real(kind=dp), intent(inout) :: x(n) + + real(kind=dp), parameter :: tol = 1d-10 ! Convergence tolerance + !eventually, omega would be defined as a function of the eigen values of A + real(kind=dp), parameter :: omega = 1.0 ! Damping factor (0 < omega < 1) + integer, parameter :: nIterMax = 20000 + + integer :: niter, i + real(kind=dp) :: diff + + logical :: lconverged + real(kind=dp) :: diag(n), x_new(n) + + diff = 0 + niter = 0 + lconverged = .false. + do while (.not. lconverged) + niter = niter + 1 + + ! Calculate the new x values using the damped Jacobi method + diag(:) = matdiag(A,n) + x_new(:) = omega * (b(:) - matmul(A,x) + x(:) * diag(:)) / diag(:) + & + (1.0_dp - omega) * x(:) + + diff = maxval(abs((x_new - x)/x_new)) + lconverged = (diff < tol) + + x = x_new + if (niter > nIterMax) exit + + end do + + return +end subroutine jacobi + +subroutine Jacobi_sparse(a,b,x,n) +!solve a system Ax=b with the iterative jacobi method for a sparse A matrix. +!This routine only iterates over x where diag(a) is != 0. + integer, intent(in) :: n + real(kind=dp) :: a(n,n), b(n) + real(kind=dp), intent(inout) :: x(n) + + real(kind=dp), parameter :: tol = 1d-10 ! Convergence tolerance + !eventually, omega would be defined as a function of the eigen values of A + real(kind=dp), parameter :: omega = 1.0 ! Damping factor (0 < omega < 1) + integer, parameter :: nIterMax = 20000 + + integer :: niter, i + real(kind=dp) :: diff + + logical :: lconverged + real(kind=dp) :: diag(n), x_new(n) + + diff = 0 + niter = 0 + lconverged = .false. + do while (.not. lconverged) + niter = niter + 1 + + ! Calculate the new x values using the damped Jacobi method + diag(:) = matdiag(A,n) + where (diag /= 0.0) + x_new(:) = omega * (b(:) - matmul(A,x) + x(:) * diag(:)) / diag(:) + & + (1.0_dp - omega) * x(:) + endwhere + diff = maxval(abs((x_new - x)/x_new),x_new > 0.0) + lconverged = (diff < tol) + + x = x_new + if (niter > nIterMax) exit + + end do + + return +end subroutine jacobi_sparse subroutine GaussSlv(a, b, n) ! Resolution d'un systeme d'equation par methode de Gauss diff --git a/src/voigts.f90 b/src/voigts.f90 index b2eba4076..610ce91a3 100644 --- a/src/voigts.f90 +++ b/src/voigts.f90 @@ -153,49 +153,4 @@ function dmax_lorentz(vth, a, eps) return end function dmax_lorentz - !building - function VoigtAller(N,a,v) - integer, intent(in) :: N - real(kind=dp), intent(in) :: a, v(N) - real(kind=dp) :: VoigtAller(N) - integer :: i,j - real(kind=dp) :: sum1, base - complex(kind=dp) :: ksi, sum2 - - if (a <= 0.1) then - do i=1,N - if (abs(v(i)) >= 2.5) then - base = (1.0+a*a*(1-2*v(i)**2))*exp(-v(i)**2) - sum1 = 0 - do j=1,7 - sum1 = sum1 + cj(j)*v(i)**(-2*(j-1)) - enddo - VoigtAller(i) = a/v(i)/v(i) * sum1 + base - else - ksi = cmplx(a, -v(i)) - sum1 = 0 - sum2 = ksi**7 - do j=1,7 - sum1 = sum1 + Aj(j)*ksi**(j-1) - sum2 = sum2 + Bj(j)*ksi**(j-1) - enddo - VoigtAller(i) = real(sum1/sum2, kind=dp) - endif - enddo - else - do i=1,N - ksi = cmplx(a, -v(i)) - sum1 = 0 - sum2 = ksi**7 - do j=1,7 - sum1 = sum1 + Aj(j)*ksi**(j-1) - sum2 = sum2 + Bj(j)*ksi**(j-1) - enddo - VoigtAller(i) = real(sum1/sum2, kind=dp) - enddo - endif - - return - end function VoigtAller - end module voigts \ No newline at end of file