source: trunk/LMDZ.COMMON/libf/evolution/orbit.F90 @ 3989

Last change on this file since 3989 was 3989, checked in by jbclement, 5 weeks ago

PEM:
Massive structural refactor of the PEM codebase for improved readability, consistency and maintainability. The goal is to modernize, standardize and consolidate the code while removing legacy complexity. In detail, this change:

  • Performs large-scale cleanup removing unused code, obsolete routines, duplicated functionality and deprecated initialization logic;
  • Removes "*_mod" wrappers;
  • Replaces mixed naming conventions with clearer variable names, domain-based file/module names and purpose-based routine names;
  • Adds native reading/writing capabilities to the PEM removing the cumbersome dependency on Mars PCM subroutines;
  • Centralizes soil/ice/orbital/PEM configuration logic into dedicated modules;
  • Simplifies routines and updates calls/interfaces to match the new structure.

This refactor significantly clarifies the codebase and provides a cleaner foundation for forthcoming developments.
JBC

File size: 13.4 KB
Line 
1MODULE orbit
2
3!=======================================================================
4!
5! Purpose: Compute the maximum number of iteration for PEM based on the
6! stopping criterion given by the orbital parameters
7!
8! Author: RV, JBC
9!=======================================================================
10
11implicit none
12
13real, dimension(:), allocatable :: year_lask  ! year before present from Laskar et al.
14real, dimension(:), allocatable :: obl_lask   ! obliquity    [deg]
15real, dimension(:), allocatable :: ecc_lask   ! eccentricity
16real, dimension(:), allocatable :: lsp_lask   ! ls perihelie [deg]
17integer                         :: iyear_lask ! Index of the line in the file year_obl_lask.asc corresponding to the closest lower year to the current year
18
19!=======================================================================
20contains
21!=======================================================================
22
23SUBROUTINE read_orbitpm(Year)
24
25use evolution, only: convert_years
26
27implicit none
28
29real, intent(in) :: Year ! Martian year of the simulation
30
31integer :: n ! number of lines in Laskar files
32integer :: ierr, i
33
34n = 0
35open(1,file = 'obl_ecc_lsp.asc',status = 'old',action = 'read')
36do
37    read(1,*,iostat = ierr)
38    if (ierr /= 0) exit
39    n = n + 1
40enddo
41close(1)
42allocate(year_lask(n),obl_lask(n),ecc_lask(n),lsp_lask(n))
43
44iyear_lask = 0
45do i = 1,size(year_lask,1)
46    read(1,*) year_lask(i), obl_lask(i), ecc_lask(i), lsp_lask(i)
47    year_lask(i) = year_lask(i)*1000./convert_years ! Conversion from Laskar's kilo Earth years to PEM Martian years
48    if (year_lask(i) > Year) iyear_lask = i + 1
49enddo
50close(1)
51if (iyear_lask == 0 .or. iyear_lask == size(year_lask,1) + 1) then
52    error stop 'The current year could not be found in the file "obl_ecc_lsp.asc".'
53else
54    write(*,*) 'The current year in the "obl_ecc_lsp.asc" file is at line:', iyear_lask
55endif
56
57END SUBROUTINE read_orbitpm
58
59!=======================================================================
60
61SUBROUTINE end_orbit
62
63implicit none
64
65if (allocated(year_lask)) deallocate(year_lask)
66if (allocated(obl_lask)) deallocate(obl_lask)
67if (allocated(ecc_lask)) deallocate(ecc_lask)
68if (allocated(lsp_lask)) deallocate(lsp_lask)
69
70END SUBROUTINE end_orbit
71!=======================================================================
72
73SUBROUTINE orbit_param_criterion(i_myear,nyears_maxorb)
74
75#ifdef CPP_IOIPSL
76    use IOIPSL,          only: getin
77#else
78    ! if not using IOIPSL, we still need to use (a local version of) getin
79    use ioipsl_getincom, only: getin
80#endif
81use planete_h,      only: e_elips, obliquit, lsperi
82use phys_constants, only: pi
83use evolution,      only: year_bp_ini, var_obl, var_ecc, var_lsp, convert_years
84
85implicit none
86
87!--------------------------------------------------------
88! Input Variables
89!--------------------------------------------------------
90real, intent(in) :: i_myear ! Martian year of the simulation
91
92!--------------------------------------------------------
93! Output Variables
94!--------------------------------------------------------
95real, intent(out) :: nyears_maxorb ! Maximum number of iteration of the PEM before orb changes too much
96
97!--------------------------------------------------------
98! Local variables
99!--------------------------------------------------------
100real                            :: Year                                           ! Year of the simulation
101real                            :: Lsp                                            ! Ls perihelion
102real                            :: max_change_obl, max_change_ecc, max_change_lsp ! Maximum admissible change
103real                            :: max_obl, max_ecc, max_lsp                      ! Maximum value of orbit param given the admissible change
104real                            :: min_obl, min_ecc, min_lsp                      ! Minimum value of orbit param given the admissible change
105real                            :: nyears_maxobl, nyears_maxecc, nyears_maxlsp       ! Maximum year iteration before reaching an inadmissible value of orbit param
106integer                         :: i                                          ! Loop variable
107real                            :: xa, xb, ya, yb                                 ! Variables for interpolation
108logical                         :: found_obl, found_ecc, found_lsp                ! Flag variables for orbital parameters
109real                            :: lsp_corr                                       ! Lsp corrected if the "modulo is crossed"
110real                            :: delta                                          ! Intermediate variable
111real, dimension(:), allocatable :: lsplask_unwrap                                 ! Unwrapped sequence of Lsp from Laskar's file
112
113! **********************************************************************
114! 0. Initializations
115! **********************************************************************
116Year = year_bp_ini + i_myear ! We compute the current year
117Lsp = lsperi*180./pi         ! We convert in degree
118
119call read_orbitpm(Year) ! Allocation of variables
120
121write(*,*) 'orbit_param_criterion, Current year =', Year
122write(*,*) 'orbit_param_criterion, Current obl. =', obliquit
123write(*,*) 'orbit_param_criterion, Current ecc. =', e_elips
124write(*,*) 'orbit_param_criterion, Current Lsp  =', Lsp
125
126! Unwrap the Lsp changes in Laskar's file
127allocate(lsplask_unwrap(iyear_lask))
128lsplask_unwrap(iyear_lask) = lsp_lask(iyear_lask)
129do i = iyear_lask,2,-1
130    delta = lsp_lask(i - 1) - lsp_lask(i)
131    if (delta > 180.) then
132        lsp_corr = lsp_lask(i - 1) - 360.
133    else if (delta < -180.) then
134        lsp_corr = lsp_lask(i - 1) + 360.
135    else
136        lsp_corr = lsp_lask(i - 1)
137    endif
138    lsplask_unwrap(i - 1) = lsplask_unwrap(i) + lsp_corr - lsp_lask(i)
139enddo
140
141! Correct the current Lsp according to the unwrapping
142delta = Lsp - lsplask_unwrap(iyear_lask)
143if (delta > 180.) then
144    Lsp = Lsp - 360.
145else if (delta < -180.) then
146    Lsp = Lsp + 360.
147endif
148
149! Constant max change case
150max_change_obl = 1.
151max_change_ecc = 5.e-3
152max_change_lsp = 20.
153
154call getin('max_change_obl',max_change_obl)
155max_obl = obliquit + max_change_obl
156min_obl = obliquit - max_change_obl
157write(*,*) 'Maximum obliquity accepted =', max_obl
158write(*,*) 'Minimum obliquity accepted =', min_obl
159
160call getin('max_change_ecc',max_change_ecc)
161max_ecc = min(e_elips + max_change_ecc,1. - 1.e-10) ! ecc < 1.
162min_ecc = max(e_elips - max_change_ecc,0.) ! ecc >= 0.
163write(*,*) 'Maximum eccentricity accepted =', max_ecc
164write(*,*) 'Minimum eccentricity accepted =', min_ecc
165
166call getin('max_change_lsp',max_change_lsp)
167max_lsp = Lsp + max_change_lsp
168min_lsp = Lsp - max_change_lsp
169write(*,*) 'Maximum Lsp accepted =', max_lsp
170write(*,*) 'Minimum Lsp accepted =', min_lsp
171! End Constant max change case
172
173! If we do not want some orb parameter to change, they should not be a stopping criterion,
174! So the number of iteration corresponding is set to maximum
175nyears_maxobl = 1000000.
176nyears_maxecc = 1000000.
177nyears_maxlsp = 1000000.
178
179! The maximum reachable year is found by interpolation according to the maximum admissible change of orbital parameters
180found_obl = .false.
181found_ecc = .false.
182found_lsp = .false.
183if (.not. var_obl) found_obl = .true.
184if (.not. var_ecc) found_ecc = .true.
185if (.not. var_lsp) found_lsp = .true.
186
187do i = iyear_lask,2,-1
188    xa = year_lask(i)
189    xb = year_lask(i - 1)
190
191    ! Obliquity
192    if (var_obl .and. .not. found_obl) then
193        ya = obl_lask(i)
194        yb = obl_lask(i - 1)
195        if (yb < min_obl) then ! The minimum accepted is overtaken
196            nyears_maxobl = (min_obl - ya)*(xb - xa)/(yb - ya) + xa - Year
197            found_obl = .true.
198        else if (max_obl < yb) then ! The maximum accepted is overtaken
199            nyears_maxobl = (max_obl - ya)*(xb - xa)/(yb - ya) + xa - Year
200            found_obl = .true.
201        endif
202    endif
203
204    ! Eccentricity
205    if (var_ecc .and. .not. found_ecc) then
206        ya = ecc_lask(i)
207        yb = ecc_lask(i - 1)
208        if (yb < min_ecc) then ! The minimum accepted is overtaken
209            nyears_maxecc = (min_ecc - ya)*(xb - xa)/(yb - ya) + xa - Year
210            found_ecc = .true.
211        else if (max_ecc < yb) then ! The maximum accepted is overtaken
212            nyears_maxecc = (max_ecc - ya)*(xb - xa)/(yb - ya) + xa - Year
213            found_ecc = .true.
214        endif
215    endif
216
217    ! Lsp
218    if (var_lsp .and. .not. found_lsp) then
219        ya = lsplask_unwrap(i)
220        yb = lsplask_unwrap(i - 1)
221        if (yb < min_lsp) then ! The minimum accepted is overtaken
222            nyears_maxlsp = (min_lsp - ya)*(xb - xa)/(yb - ya) + xa - Year
223            found_lsp = .true.
224        else if (max_lsp < yb) then ! The maximum accepted is overtaken
225            nyears_maxlsp = (max_lsp - ya)*(xb - xa)/(yb - ya) + xa - Year
226            found_lsp = .true.
227        endif
228    endif
229
230    if (found_obl .and. found_ecc .and. found_lsp) exit ! The loop is left as soon as everything is found
231enddo
232
233deallocate(lsplask_unwrap)
234
235if (.not. found_obl) write(*,*) 'The maximum reachable year for obl. could not be found in the file "obl_ecc_lsp.asc".'
236if (.not. found_ecc) write(*,*) 'The maximum reachable year for ecc. could not be found in the file "obl_ecc_lsp.asc".'
237if (.not. found_lsp) write(*,*) 'The maximum reachable year for Lsp could not be found in the file "obl_ecc_lsp.asc".'
238
239write(*,*) 'Max. number of years for the obl. parameter =', nyears_maxobl
240write(*,*) 'Max. number of years for the ecc. parameter =', nyears_maxecc
241write(*,*) 'Max. number of years for the Lsp parameter  =', nyears_maxlsp
242
243nyears_maxorb = min(nyears_maxobl,nyears_maxecc,nyears_maxlsp)
244if (nyears_maxorb < 1.) nyears_maxorb = 1. ! nyears_maxorb should be at least equal to 1
245write(*,*) 'So the max. number of years for the orbital criteria =', nyears_maxorb
246
247END SUBROUTINE orbit_param_criterion
248
249!***********************************************************************
250! Purpose: Recompute orbit parameters based on values by Laskar et al.
251SUBROUTINE set_new_orbitpm(i_myear,i_myear_leg)
252
253use evolution,        only: year_bp_ini, var_obl, var_ecc, var_lsp
254use phys_constants,   only: pi
255use planete_h,        only: e_elips, obliquit, lsperi, periheli, aphelie, p_elips, peri_day, year_day
256use call_dayperi_mod, only: call_dayperi
257
258implicit none
259
260!--------------------------------------------------------
261! Input Variables
262!--------------------------------------------------------
263real, intent(in) :: i_myear     ! Number of simulated Martian years
264real, intent(in) :: i_myear_leg ! Number of iterations of the PEM
265
266!--------------------------------------------------------
267! Output Variables
268!--------------------------------------------------------
269
270!--------------------------------------------------------
271! Local variables
272!--------------------------------------------------------
273real    :: Year           ! Year of the simulation
274real    :: Lsp            ! Ls perihelion
275integer :: i          ! Loop variable
276real    :: halfaxe        ! Million of km
277real    :: unitastr
278real    :: xa, xb, ya, yb ! Variables for interpolation
279logical :: found_year     ! Flag variable
280
281! **********************************************************************
282! 0. Initializations
283! **********************************************************************
284Year = year_bp_ini + i_myear ! We compute the current year
285Lsp = lsperi*180./pi         ! We convert in degree
286
287write(*,*) 'set_new_orbitpm, Old year =', Year - i_myear_leg
288write(*,*) 'set_new_orbitpm, Old obl. =', obliquit
289write(*,*) 'set_new_orbitpm, Old ecc. =', e_elips
290write(*,*) 'set_new_orbitpm, Old Lsp  =', Lsp
291write(*,*) 'set_new_orbitpm, New year =', Year
292
293found_year = .false.
294if (Year < 0.) then
295    do i = iyear_lask,2,-1
296        xa = year_lask(i)
297        xb = year_lask(i - 1)
298        if (xa <= Year .and. Year < xb) then
299            ! Obliquity
300            if (var_obl) then
301                ya = obl_lask(i)
302                yb = obl_lask(i - 1)
303                obliquit = (Year - xa)*(yb - ya)/(xb - xa) + ya
304            endif
305
306            ! Eccentricity
307            if (var_ecc) then
308                ya = ecc_lask(i)
309                yb = ecc_lask(i - 1)
310                e_elips = (Year - xa)*(yb - ya)/(xb - xa) + ya
311            endif
312
313            ! Lsp
314            if (var_lsp) then
315                ya = lsp_lask(i)
316                yb = lsp_lask(i - 1)
317                if (abs(yb - ya) > 300.) then ! If modulo is "crossed" through the interval
318                    if (ya < yb) then ! Lsp should be decreasing
319                        ya = ya + 360.
320                    else ! Lsp should be increasing
321                        yb = yb + 360.
322                    endif
323                endif
324                Lsp = modulo((Year - xa)*(yb - ya)/(xb - xa) + ya,360.)
325            endif
326            found_year = .true.
327            exit ! The loop is left as soon as the right interval is found
328        endif
329    enddo
330else if (Year >= 0 .and. Year < 100.) then ! For present orbital characteristics
331    if (var_obl) obliquit = 25.19
332    if (var_ecc) e_elips = 0.0934
333    if (var_lsp) Lsp = 251.
334    found_year = .true.
335endif
336
337if (.not. found_year) error stop 'The new year could not be found in the file "obl_ecc_lsp.asc".'
338
339write(*,*) 'set_new_orbitpm, New obl. =', obliquit
340write(*,*) 'set_new_orbitpm, New ecc. =', e_elips
341write(*,*) 'set_new_orbitpm, New Lsp  =', Lsp
342
343halfaxe = 227.94
344Lsp = Lsp*pi/180.
345periheli = halfaxe*(1 - e_elips)
346aphelie =  halfaxe*(1 + e_elips)
347unitastr = 149.597927
348p_elips = 0.5*(periheli + aphelie)*(1. - e_elips*e_elips)/unitastr
349
350call call_dayperi(Lsp,e_elips,peri_day,year_day)
351
352call end_orbit()
353
354END SUBROUTINE set_new_orbitpm
355
356END MODULE orbit
357
Note: See TracBrowser for help on using the repository browser.