Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
csst-sims
csst_msc_sim
Commits
06867319
Commit
06867319
authored
May 09, 2024
by
Liu Dezi
Browse files
Upload New File
parent
47a3d5d5
Pipeline
#4409
failed with stage
in 0 seconds
Changes
1
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
tests/test_field_distortion.py
0 → 100644
View file @
06867319
import
unittest
import
numpy
as
np
import
galsim
import
os
import
sys
from
astropy.table
import
Table
from
scipy
import
interpolate
import
pickle
class
test_field_distortion
(
unittest
.
TestCase
):
def
__init__
(
self
,
methodName
=
"runTest"
):
super
(
test_field_distortion
,
self
).
__init__
(
methodName
)
self
.
dataMainPath
=
os
.
path
.
join
(
os
.
getenv
(
"UNIT_TEST_DATA_ROOT"
),
"csst_msc_sim/field_distortion"
)
self
.
dataInputPath
=
os
.
path
.
join
(
self
.
dataMainPath
,
"input_catalog"
)
self
.
fdModelName
=
"FieldDistModel_v2.0_test.pickle"
def
test_fd_model
(
self
):
cat_dir
=
self
.
dataInputPath
model_dir
=
self
.
dataMainPath
model_date
=
"2024-05-08"
model_name
=
self
.
fdModelName
field_distortion_model
(
cat_dir
,
model_dir
,
poly_degree
=
4
,
model_date
=
model_date
,
model_name
=
model_name
,
)
def
test_fd_apply
(
self
):
model_name
=
self
.
fdModelName
model_dir
=
self
.
dataMainPath
cat_dir
=
self
.
dataMainPath
field_distortion_apply
(
model_name
,
model_dir
,
cat_dir
,
ra_cen
=
60.0
,
dec_cen
=-
40.0
,
img_rot
=
0.0
)
def
ccdParam
():
"""
Basic CCD size and noise parameters.
"""
# CCD size
xt
,
yt
=
59516
,
49752
x0
,
y0
=
9216
,
9232
xgap
,
ygap
=
(
534
,
1309
),
898
xnchip
,
ynchip
=
6
,
5
ccdSize
=
xt
,
yt
,
x0
,
y0
,
xgap
,
ygap
,
xnchip
,
ynchip
# other parameters
readNoise
=
5.0
# e/pix
darkNoise
=
0.02
# e/pix/s
pixel_scale
=
0.074
# pixel scale
gain
=
1.0
ccdBase
=
readNoise
,
darkNoise
,
pixel_scale
,
gain
return
ccdSize
,
ccdBase
def
chipLim
(
chip
):
ccdSize
,
ccdBase
=
ccdParam
()
xt
,
yt
,
x0
,
y0
,
gx
,
gy
,
xnchip
,
ynchip
=
ccdSize
gx1
,
gx2
=
gx
rowID
=
((
chip
-
1
)
%
5
)
+
1
colID
=
6
-
((
chip
-
1
)
//
5
)
xrem
=
2
*
(
colID
-
1
)
-
(
xnchip
-
1
)
xcen
=
(
x0
//
2
+
gx1
//
2
)
*
xrem
if
chip
<=
5
or
chip
==
10
:
xcen
=
(
x0
//
2
+
gx1
//
2
)
*
xrem
+
(
gx2
-
gx1
)
if
chip
>=
26
or
chip
==
21
:
xcen
=
(
x0
//
2
+
gx1
//
2
)
*
xrem
-
(
gx2
-
gx1
)
nx0
=
xcen
-
x0
//
2
+
1
nx1
=
xcen
+
x0
//
2
yrem
=
(
rowID
-
1
)
-
ynchip
//
2
ycen
=
(
y0
+
gy
)
*
yrem
ny0
=
ycen
-
y0
//
2
+
1
ny1
=
ycen
+
y0
//
2
return
nx0
,
nx1
,
ny0
,
ny1
def
chip_filter
(
nchip
):
"""
return filter name of a given chip
"""
filtype
=
[
"nuv"
,
"u"
,
"g"
,
"r"
,
"i"
,
"z"
,
"y"
]
# updated configurations
# if nchip>24 or nchip<7: raise ValueError("!!! Chip ID: [7,24]")
if
nchip
in
[
6
,
15
,
16
,
25
]:
filter_name
=
"y"
if
nchip
in
[
11
,
20
]:
filter_name
=
"z"
if
nchip
in
[
7
,
24
]:
filter_name
=
"i"
if
nchip
in
[
14
,
17
]:
filter_name
=
"u"
if
nchip
in
[
9
,
22
]:
filter_name
=
"r"
if
nchip
in
[
12
,
13
,
18
,
19
]:
filter_name
=
"nuv"
if
nchip
in
[
8
,
23
]:
filter_name
=
"g"
filter_id
=
filtype
.
index
(
filter_name
)
return
filter_id
,
filter_name
def
skyLim
(
wcs
,
x0
,
x1
,
y0
,
y1
):
"""
The sky coverage of a single exposure image
"""
r2d
=
180.0
/
np
.
pi
# xt, yt, x0, y0, gx, gy, xnchip, ynchip = ccdSize()
s1
=
wcs
.
toWorld
(
galsim
.
PositionD
(
x0
,
y0
))
s2
=
wcs
.
toWorld
(
galsim
.
PositionD
(
x0
,
y1
))
s3
=
wcs
.
toWorld
(
galsim
.
PositionD
(
x1
,
y0
))
s4
=
wcs
.
toWorld
(
galsim
.
PositionD
(
x1
,
y1
))
ra
=
[
s1
.
ra
.
rad
*
r2d
,
s2
.
ra
.
rad
*
r2d
,
s3
.
ra
.
rad
*
r2d
,
s4
.
ra
.
rad
*
r2d
]
dec
=
[
s1
.
dec
.
rad
*
r2d
,
s2
.
dec
.
rad
*
r2d
,
s3
.
dec
.
rad
*
r2d
,
s4
.
dec
.
rad
*
r2d
]
return
min
(
ra
),
max
(
ra
),
min
(
dec
),
max
(
dec
)
def
wcsMain
(
imgRotation
=
0.0
,
raCenter
=
0.0
,
decCenter
=
0.0
):
ccdSize
,
ccdBase
=
ccdParam
()
xsize
,
ysize
,
_
,
_
,
_
,
_
,
_
,
_
=
ccdSize
_
,
_
,
pixelScale
,
_
=
ccdBase
xmcen
,
ymcen
=
0.0
,
0.0
imrot
=
imgRotation
*
galsim
.
degrees
racen
=
raCenter
*
galsim
.
degrees
deccen
=
decCenter
*
galsim
.
degrees
# define the wcs
dudx
=
-
np
.
cos
(
imrot
.
rad
)
*
pixelScale
dudy
=
+
np
.
sin
(
imrot
.
rad
)
*
pixelScale
dvdx
=
-
np
.
sin
(
imrot
.
rad
)
*
pixelScale
dvdy
=
-
np
.
cos
(
imrot
.
rad
)
*
pixelScale
moscen
=
galsim
.
PositionD
(
x
=
xmcen
,
y
=
ymcen
)
skyCenter
=
galsim
.
CelestialCoord
(
ra
=
racen
,
dec
=
deccen
)
affine
=
galsim
.
AffineTransform
(
dudx
,
dudy
,
dvdx
,
dvdy
,
origin
=
moscen
)
wcs
=
galsim
.
TanWCS
(
affine
,
skyCenter
,
units
=
galsim
.
arcsec
)
return
wcs
# FD model
def
field_distortion_model
(
cat_dir
,
model_dir
,
poly_degree
=
4
,
model_date
=
"2024-05-08"
,
model_name
=
"FieldDistModel_v2.0_test.pickle"
,
):
# default parameter setup
nccd
,
nwave
,
npsf
=
30
,
4
,
30
*
30
# load a CSST-like wcs
wcs
=
wcsMain
()
cd11
,
cd12
=
wcs
.
cd
[
0
,
0
],
wcs
.
cd
[
0
,
1
]
cd21
,
cd22
=
wcs
.
cd
[
1
,
0
],
wcs
.
cd
[
1
,
1
]
xmcen
,
ymcen
=
wcs
.
crpix
# obtain the interpolation model
fdFunList
=
{}
fdFunList
[
"date"
]
=
model_date
for
iwave
in
range
(
1
,
nwave
+
1
):
# if iwave!=1: continue
iwaveKey
=
"wave%d"
%
iwave
# first construct the global interpolation
xwList
,
ywList
=
[],
[]
xdList
,
ydList
=
[],
[]
fdFunList
[
iwaveKey
]
=
{}
for
iccd
in
range
(
1
,
nccd
+
1
):
# if iccd!=9: continue
iccdKey
=
"ccd"
+
str
(
"0%d"
%
(
iccd
))[
-
2
:]
# load PSF data
ipsfDatn
=
cat_dir
+
"ccd%d_%s.dat"
%
(
iccd
,
iwaveKey
)
ipsfDat
=
Table
.
read
(
ipsfDatn
,
format
=
"ascii"
)
for
ipsf
in
range
(
1
,
npsf
+
1
):
# if ipsf!=2: continue
xField
=
ipsfDat
[
"field_x"
][
ipsf
-
1
]
yField
=
ipsfDat
[
"field_y"
][
ipsf
-
1
]
# image coordinate with field distortion
xImage
=
100.0
*
(
ipsfDat
[
"image_x"
][
ipsf
-
1
]
+
ipsfDat
[
"centroid_x"
][
ipsf
-
1
]
)
yImage
=
100.0
*
(
ipsfDat
[
"image_y"
][
ipsf
-
1
]
+
ipsfDat
[
"centroid_y"
][
ipsf
-
1
]
)
# image coordinate only with wcs projection
xwcs
=
(
cd12
*
yField
-
cd22
*
xField
)
/
(
cd12
*
cd21
-
cd11
*
cd22
)
+
xmcen
ywcs
=
(
cd21
*
xField
-
cd11
*
yField
)
/
(
cd12
*
cd21
-
cd11
*
cd22
)
+
ymcen
xwList
+=
[
xwcs
]
ywList
+=
[
ywcs
]
xdList
+=
[
xImage
]
ydList
+=
[
yImage
]
# global interpolation
xImageFun
=
interpolate
.
SmoothBivariateSpline
(
xwList
,
ywList
,
xdList
,
kx
=
poly_degree
,
ky
=
poly_degree
)
yImageFun
=
interpolate
.
SmoothBivariateSpline
(
xwList
,
ywList
,
ydList
,
kx
=
poly_degree
,
ky
=
poly_degree
)
fdFunList
[
iwaveKey
]
=
{
"xImagePos"
:
xImageFun
,
"yImagePos"
:
yImageFun
,
"interpLimit"
:
[
np
.
min
(
xwList
),
np
.
max
(
xwList
),
np
.
min
(
ywList
),
np
.
max
(
ywList
),
],
}
# construct the residual interpolation
fdFunList
[
iwaveKey
][
"residual"
]
=
{}
for
iccd
in
range
(
1
,
nccd
+
1
):
# if iccd!=1: continue
iccdKey
=
"ccd"
+
str
(
"0%d"
%
(
iccd
))[
-
2
:]
# open the ditortion data
ipsfDatn
=
cat_dir
+
"ccd%d_%s.dat"
%
(
iccd
,
iwaveKey
)
ipsfDat
=
Table
.
read
(
ipsfDatn
,
format
=
"ascii"
)
ixwList
,
iywList
=
[],
[]
idxList
,
idyList
=
[],
[]
for
ipsf
in
range
(
1
,
npsf
+
1
):
# if ipsf!=1: continue
print
(
"^_^ loading: iccd-{:} iwave-{:} ipsf-{:}"
.
format
(
iccd
,
iwave
,
ipsf
)
)
xField
=
ipsfDat
[
"field_x"
][
ipsf
-
1
]
yField
=
ipsfDat
[
"field_y"
][
ipsf
-
1
]
xImage
=
100.0
*
(
ipsfDat
[
"image_x"
][
ipsf
-
1
]
+
ipsfDat
[
"centroid_x"
][
ipsf
-
1
]
)
yImage
=
100.0
*
(
ipsfDat
[
"image_y"
][
ipsf
-
1
]
+
ipsfDat
[
"centroid_y"
][
ipsf
-
1
]
)
# image coordinate only with wcs projection
xwcs
=
(
cd12
*
yField
-
cd22
*
xField
)
/
(
cd12
*
cd21
-
cd11
*
cd22
)
+
xmcen
ywcs
=
(
cd21
*
xField
-
cd11
*
yField
)
/
(
cd12
*
cd21
-
cd11
*
cd22
)
+
ymcen
ixPred
=
xImageFun
(
xwcs
,
ywcs
)[
0
][
0
]
iyPred
=
yImageFun
(
xwcs
,
ywcs
)[
0
][
0
]
idx
=
xImage
-
ixPred
idy
=
yImage
-
iyPred
# print(idx, idy)
ixwList
+=
[
xwcs
]
iywList
+=
[
ywcs
]
idxList
+=
[
idx
]
idyList
+=
[
idy
]
# interpolation
xResFun
=
interpolate
.
SmoothBivariateSpline
(
ixwList
,
iywList
,
idxList
,
kx
=
poly_degree
,
ky
=
poly_degree
)
yResFun
=
interpolate
.
SmoothBivariateSpline
(
ixwList
,
iywList
,
idyList
,
kx
=
poly_degree
,
ky
=
poly_degree
)
fdFunList
[
iwaveKey
][
"residual"
][
iccdKey
]
=
{
"xResidual"
:
xResFun
,
"yResidual"
:
yResFun
,
"interpLimit"
:
[
np
.
min
(
ixwList
),
np
.
max
(
ixwList
),
np
.
min
(
iywList
),
np
.
max
(
iywList
),
],
}
# save the interpolation functions
model_name_full
=
os
.
path
.
join
(
model_dir
,
model_name
)
with
open
(
model_name_full
,
"wb"
)
as
out
:
pickle
.
dump
(
fdFunList
,
out
,
pickle
.
HIGHEST_PROTOCOL
)
return
def
field_distortion_apply
(
model_name
,
model_dir
,
cat_dir
,
ra_cen
=
60.0
,
dec_cen
=-
40.0
,
img_rot
=
0.0
):
# CCD and observation
ccdSize
,
ccdBase
=
ccdParam
()
xsize
,
ysize
,
xchip
,
ychip
,
xgap
,
ygap
,
xnchip
,
ynchip
=
ccdSize
nchip
=
xnchip
*
ynchip
#################################################
xmcen
,
ymcen
=
0.0
,
0.0
#################################################
badchip
=
list
(
range
(
1
,
6
))
+
list
(
range
(
26
,
31
))
+
[
10
,
21
]
# define the wcs of the image mosaic
print
(
"^_^ Construct the wcs of the entire image mosaic using Gnomonic/TAN projection"
)
wcs
=
wcsMain
(
imgRotation
=
img_rot
,
raCenter
=
ra_cen
,
decCenter
=
dec_cen
)
#################################################
# load the field distortion model
model_name_full
=
os
.
path
.
join
(
model_dir
,
model_name
)
with
open
(
model_name_full
,
"rb"
)
as
f
:
fdModel
=
pickle
.
load
(
f
)
#################################################
raLow
,
raUp
,
decLow
,
decUp
=
skyLim
(
wcs
,
-
xsize
//
2
+
1
,
xsize
//
2
,
-
ysize
//
2
+
1
,
ysize
//
2
)
dra
=
(
raUp
-
raLow
)
*
np
.
cos
(
dec_cen
*
np
.
pi
/
180.0
)
ddec
=
decUp
-
decLow
print
(
" Image pixel size: %d*%d; center: (Ra, Dec)=(%.3f, %.3f)."
%
(
xsize
,
ysize
,
ra_cen
,
dec_cen
)
)
print
(
" Field of Veiw: %.2f * %.2f deg^2."
%
(
dra
,
ddec
))
# filters and corresponding bounds in the image mosaic
fbound
=
{}
print
(
" Model the filter distributions in the image mosaic ..."
)
stats
=
{}
for
i
in
range
(
nchip
):
chip_id
=
i
+
1
if
chip_id
in
badchip
:
continue
cx0
,
cx1
,
cy0
,
cy1
=
chipLim
(
chip_id
)
chip_bound
=
galsim
.
BoundsD
(
cx0
-
1
,
cx1
-
1
,
cy0
-
1
,
cy1
-
1
)
chip_filter_id
,
chip_filt
=
chip_filter
(
chip_id
)
# print "^_^ CHIP %d, Filter %s"%(chip_id,chip_filter)
fbound
[
chip_id
]
=
[
chip_filter_id
,
chip_filt
,
chip_bound
]
stats
[
chip_id
]
=
[
0
,
0
,
0
]
# generate object grid
ra_input
=
np
.
arange
(
ra_cen
-
1.0
,
ra_cen
+
1.0
,
0.00125
)
dec_input
=
np
.
arange
(
dec_cen
-
1.0
,
dec_cen
+
1.0
,
0.00125
)
nobj
=
len
(
ra_input
)
*
len
(
dec_input
)
crdCat
=
np
.
zeros
((
nobj
,
2
))
cid
=
0
for
id1
in
range
(
len
(
ra_input
)):
ira
=
ra_input
[
id1
]
for
id2
in
range
(
len
(
dec_input
)):
idec
=
dec_input
[
id2
]
crdCat
[
cid
,
:]
=
ira
,
idec
cid
+=
1
print
(
"^_^ Total %d objects are generaged"
%
nobj
)
# main program
for
i
in
range
(
nchip
):
# if i not in [6]: continue
if
i
+
1
in
badchip
:
continue
filtidk
,
filtnmk
,
boundk
=
fbound
[
i
+
1
]
idStr
=
str
(
"0%d"
%
(
i
+
1
))[
-
2
:]
###################################################################
# 1) Use global field distortion model: FieldDistModelGlobal_v2.0.pickle
ifdModel
=
fdModel
[
"wave1"
]
irsModel
=
fdModel
[
"wave1"
][
"residual"
][
"ccd"
+
idStr
]
xLowI
,
xUpI
,
yLowI
,
yUpI
=
ifdModel
[
"interpLimit"
]
xlLowI
,
xlUpI
,
ylLowI
,
ylUpI
=
irsModel
[
"interpLimit"
]
# field distortion model along x/y-axis
ixfdModel
=
ifdModel
[
"xImagePos"
]
iyfdModel
=
ifdModel
[
"yImagePos"
]
ixrsModel
=
irsModel
[
"xResidual"
]
iyrsModel
=
irsModel
[
"yResidual"
]
# first-order derivatives of the global field distortion model
ifx_dx
=
ixfdModel
.
partial_derivative
(
1
,
0
)
ifx_dy
=
ixfdModel
.
partial_derivative
(
0
,
1
)
ify_dx
=
iyfdModel
.
partial_derivative
(
1
,
0
)
ify_dy
=
iyfdModel
.
partial_derivative
(
0
,
1
)
# first-order derivatives of the residual field distortion model
irx_dx
=
ixrsModel
.
partial_derivative
(
1
,
0
)
irx_dy
=
ixrsModel
.
partial_derivative
(
0
,
1
)
iry_dx
=
iyrsModel
.
partial_derivative
(
1
,
0
)
iry_dy
=
iyrsModel
.
partial_derivative
(
0
,
1
)
###################################################################
# construct the image mosaic firstly
xorigin
,
yorigin
=
xmcen
-
boundk
.
xmin
,
ymcen
-
boundk
.
ymin
print
(
" Construct the chip mosaic ..."
)
fimage
=
galsim
.
ImageF
(
xchip
,
ychip
)
fimage
.
setOrigin
(
boundk
.
xmin
,
boundk
.
ymin
)
fimage
.
wcs
=
wcs
raLow
,
raUp
,
decLow
,
decUp
=
skyLim
(
wcs
,
boundk
.
xmin
,
boundk
.
xmax
,
boundk
.
ymin
,
boundk
.
ymax
)
dra
=
(
raUp
-
raLow
)
*
np
.
cos
(
dec_cen
*
np
.
pi
/
180.0
)
ddec
=
decUp
-
decLow
print
(
" Image coverage: %.2f * %.2f arcmin^2."
%
(
dra
*
60.0
,
ddec
*
60.0
))
# enlarge the sky coverage in order to catch the galaxies at the chip edge
raLow
-=
0.2
/
60.0
decLow
-=
0.2
/
60.0
raUp
+=
0.2
/
60.0
decUp
+=
0.2
/
60.0
print
(
" Range: RA=[%.4f, %.4f]; DEC=[%.4f, %.4f]"
%
(
raLow
,
raUp
,
decLow
,
decUp
)
)
# generate the galaxy and star images
catxxn
=
os
.
path
.
join
(
cat_dir
,
"csst_mainfocus_field_distortion_ccd%s_%s.cat"
%
(
idStr
,
filtnmk
)
)
hdrxx
=
"#id_obj id_chip filter ra_true dec_ture x_image_ture y_image_ture x_image y_image g1_fd g2_fd
\n
"
fmtxx
=
"%8d %3d %4s %12.6f %12.6f %13.6f %13.6f %13.6f %13.6f %9.5f %9.5f
\n
"
catxx
=
open
(
catxxn
,
"w"
)
catxx
.
write
(
hdrxx
)
oidxx
=
0
for
k
in
range
(
nobj
):
# if k != 0: continue
# input galaxy parameters
rak
=
crdCat
[
k
,
0
]
deck
=
crdCat
[
k
,
1
]
# reject objects out of the image
if
(
rak
-
raLow
)
*
(
rak
-
raUp
)
>
0.0
or
(
deck
-
decLow
)
*
(
deck
-
decUp
)
>
0.0
:
continue
world_pos
=
galsim
.
CelestialCoord
(
ra
=
rak
*
galsim
.
degrees
,
dec
=
deck
*
galsim
.
degrees
)
image_pos
=
fimage
.
wcs
.
toImage
(
world_pos
)
xk_true
=
image_pos
.
x
yk_true
=
image_pos
.
y
#################################################################
# field distortion
if
(
xLowI
-
xk_true
)
*
(
xUpI
-
xk_true
)
>
0
or
(
yLowI
-
yk_true
)
*
(
yUpI
-
yk_true
)
>
0
:
continue
xk
=
ixfdModel
(
xk_true
,
yk_true
)[
0
][
0
]
yk
=
iyfdModel
(
xk_true
,
yk_true
)[
0
][
0
]
# global offset correction
if
(
xlLowI
-
xk
)
*
(
xlUpI
-
xk
)
>
0
or
(
ylLowI
-
yk
)
*
(
ylUpI
-
yk
)
>
0
:
continue
dxk
=
ixrsModel
(
xk
,
yk
)[
0
][
0
]
dyk
=
iyrsModel
(
xk
,
yk
)[
0
][
0
]
xk
=
xk
+
dxk
yk
=
yk
+
dyk
# field distortion induced ellipticity
ix_dx
=
ifx_dx
(
xk
,
yk
)
+
irx_dx
(
xk
,
yk
)
ix_dy
=
ifx_dy
(
xk
,
yk
)
+
irx_dy
(
xk
,
yk
)
iy_dx
=
ify_dx
(
xk
,
yk
)
+
iry_dx
(
xk
,
yk
)
iy_dy
=
ify_dy
(
xk
,
yk
)
+
iry_dy
(
xk
,
yk
)
g1k_fd
=
0.0
+
(
iy_dy
-
ix_dx
)
/
(
iy_dy
+
ix_dx
)
g2k_fd
=
0.0
-
(
iy_dx
+
ix_dy
)
/
(
iy_dy
+
ix_dx
)
#################################################################
dxk_true
,
dyk_true
=
xk_true
-
xmcen
,
yk_true
-
ymcen
xLock_true
,
yLock_true
=
dxk_true
+
xorigin
+
1.0
,
dyk_true
+
yorigin
+
1.0
dxk
,
dyk
=
xk
-
xmcen
,
yk
-
ymcen
xLock
,
yLock
=
dxk
+
xorigin
+
1.0
,
dyk
+
yorigin
+
1.0
if
(
xLock_true
<
0
)
or
(
xLock_true
>
xchip
):
continue
if
(
yLock_true
<
0
)
or
(
yLock_true
>
ychip
):
continue
if
(
xLock
<
0
)
or
(
xLock
>
xchip
):
continue
if
(
yLock
<
0
)
or
(
yLock
>
ychip
):
continue
linexx
=
fmtxx
%
(
k
+
1
,
i
+
1
,
filtnmk
.
lower
(),
rak
,
deck
,
xLock_true
,
yLock_true
,
xLock
,
yLock
,
g1k_fd
[
0
][
0
],
g2k_fd
[
0
][
0
],
)
catxx
.
write
(
linexx
)
catxx
.
close
()
return
if
__name__
==
"__main__"
:
unittest
.
main
()
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment