Skip to content

Commit

Permalink
Merge branch 'feature/devList' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
theurich committed Oct 18, 2023
2 parents c5410ff + 8d1ee58 commit 3a1ad6f
Show file tree
Hide file tree
Showing 14 changed files with 692 additions and 96 deletions.
38 changes: 37 additions & 1 deletion build/common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,38 @@ ESMF_F90LINKRPATHSTHIRD += $(ESMF_F90RPATHPREFIX)$(ESMF_BABELTRACE_LIBPATH)
endif
endif

#-------------------------------------------------------------------------------
# NVML
#-------------------------------------------------------------------------------
ifeq ($(ESMF_NVML),ON)
ESMF_NVML = standard
endif
ifeq ($(ESMF_NVML),standard)
ifneq ($(origin ESMF_NVML_LIBS), environment)
ESMF_NVML_LIBS = -lnvidia-ml
endif
endif

ifdef ESMF_NVML
ESMF_CPPFLAGS += -DESMF_NVML=1
ifdef ESMF_NVML_INCLUDE
ESMF_CXXCOMPILEPATHSTHIRD += -I$(ESMF_NVML_INCLUDE)
ESMF_F90COMPILEPATHSTHIRD += -I$(ESMF_NVML_INCLUDE)
endif
ifdef ESMF_NVML_LIBS
ESMF_CXXLINKLIBS += $(ESMF_NVML_LIBS)
ESMF_CXXLINKRPATHSTHIRD += $(addprefix $(ESMF_CXXRPATHPREFIX),$(subst -L,,$(filter -L%,$(ESMF_NVML_LIBS))))
ESMF_F90LINKLIBS += $(ESMF_NVML_LIBS)
ESMF_F90LINKRPATHSTHIRD += $(addprefix $(ESMF_F90RPATHPREFIX),$(subst -L,,$(filter -L%,$(ESMF_NVML_LIBS))))
endif
ifdef ESMF_NVML_LIBPATH
ESMF_CXXLINKPATHSTHIRD += -L$(ESMF_NVML_LIBPATH)
ESMF_F90LINKPATHSTHIRD += -L$(ESMF_NVML_LIBPATH)
ESMF_CXXLINKRPATHSTHIRD += $(ESMF_CXXRPATHPREFIX)$(ESMF_NVML_LIBPATH)
ESMF_F90LINKRPATHSTHIRD += $(ESMF_F90RPATHPREFIX)$(ESMF_NVML_LIBPATH)
endif
endif

#-------------------------------------------------------------------------------
# Set the correct MPIRUN command with appropriate options
#-------------------------------------------------------------------------------
Expand All @@ -1820,7 +1852,11 @@ ifeq ($(ESMF_OPENMP),OFF)
ESMF_CPPFLAGS += -DESMF_NO_OPENMP
endif

ifeq ($(ESMF_OPENMP),ON)
ifeq ($(ESMF_OPENMP),OMP4)
ESMF_CPPFLAGS += -DESMF_OPENMP4
endif

ifneq ($(ESMF_OPENMP),OFF)
ESMF_F90COMPILEOPTS += $(ESMF_OPENMP_F90COMPILEOPTS)
ESMF_F90LINKOPTS += $(ESMF_OPENMP_F90LINKOPTS)
ESMF_CXXCOMPILEOPTS += $(ESMF_OPENMP_CXXCOMPILEOPTS)
Expand Down
5 changes: 5 additions & 0 deletions build_config/Linux.nvhpc.default/build_rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,11 @@ ESMF_CXXLINKOPTS += -Wl,--no-as-needed
############################################################
# Shared library options
#
ifeq ($(ESMF_OPENACC),ON)
# Currently accelerator code is not supported inside shared libraries.
# Turn off shared lib build if OpenACC code active inside ESMF.
ESMF_SL_LIBS_TO_MAKE =
endif
ESMF_SL_LIBOPTS += -shared

############################################################
Expand Down
5 changes: 5 additions & 0 deletions build_config/Unicos.nvhpc.default/build_rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ ESMF_CXXLINKOPTS += -Wl,--no-as-needed
############################################################
# Shared library options
#
ifeq ($(ESMF_OPENACC),ON)
# Currently accelerator code is not supported inside shared libraries.
# Turn off shared lib build if OpenACC code active inside ESMF.
ESMF_SL_LIBS_TO_MAKE =
endif
ESMF_SL_LIBOPTS += -shared

############################################################
Expand Down
76 changes: 56 additions & 20 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -255,15 +255,15 @@ endif
fi; \
fi
-@if [ -n "$(ESMF_XERCES)" ] ; then \
echo "ESMF_XERCES: $(ESMF_XERCES)" ; \
echo "ESMF_XERCES: $(ESMF_XERCES)" ; \
if [ -n "$(ESMF_XERCES_INCLUDE)" ] ; then \
echo "ESMF_XERCES_INCLUDE: $(ESMF_XERCES_INCLUDE)" ; \
echo "ESMF_XERCES_INCLUDE: $(ESMF_XERCES_INCLUDE)" ; \
fi; \
if [ -n "$(ESMF_XERCES_LIBS)" ] ; then \
echo "ESMF_XERCES_LIBS: $(ESMF_XERCES_LIBS)" ; \
echo "ESMF_XERCES_LIBS: $(ESMF_XERCES_LIBS)" ; \
fi; \
if [ -n "$(ESMF_XERCES_LIBPATH)" ] ; then \
echo "ESMF_XERCES_LIBPATH: $(ESMF_XERCES_LIBPATH)" ; \
echo "ESMF_XERCES_LIBPATH: $(ESMF_XERCES_LIBPATH)" ; \
fi; \
fi
-@if [ -n "$(ESMF_YAMLCPP)" ] ; then \
Expand All @@ -279,27 +279,39 @@ endif
fi; \
fi
-@if [ -n "$(ESMF_PROJ4)" ] ; then \
echo "ESMF_PROJ4: $(ESMF_PROJ4)" ; \
echo "ESMF_PROJ4: $(ESMF_PROJ4)" ; \
if [ -n "$(ESMF_PROJ4_INCLUDE)" ] ; then \
echo "ESMF_PROJ4_INCLUDE: $(ESMF_PROJ4_INCLUDE)" ; \
echo "ESMF_PROJ4_INCLUDE: $(ESMF_PROJ4_INCLUDE)" ; \
fi; \
if [ -n "$(ESMF_PROJ4_LIBS)" ] ; then \
echo "ESMF_PROJ4_LIBS: $(ESMF_PROJ4_LIBS)" ; \
echo "ESMF_PROJ4_LIBS: $(ESMF_PROJ4_LIBS)" ; \
fi; \
if [ -n "$(ESMF_PROJ4_LIBPATH)" ] ; then \
echo "ESMF_PROJ4_LIBPATH: $(ESMF_PROJ4_LIBPATH)" ; \
echo "ESMF_PROJ4_LIBPATH: $(ESMF_PROJ4_LIBPATH)" ; \
fi; \
fi
-@if [ -n "$(ESMF_BABELTRACE)" ] ; then \
echo "ESMF_BABELTRACE: $(ESMF_BABELTRACE)" ; \
echo "ESMF_BABELTRACE: $(ESMF_BABELTRACE)" ; \
if [ -n "$(ESMF_BABELTRACE_INCLUDE)" ] ; then \
echo "ESMF_BABELTRACE_INCLUDE: $(ESMF_BABELTRACE_INCLUDE)" ; \
echo "ESMF_BABELTRACE_INCLUDE: $(ESMF_BABELTRACE_INCLUDE)" ; \
fi; \
if [ -n "$(ESMF_BABELTRACE_LIBS)" ] ; then \
echo "ESMF_BABELTRACE_LIBS: $(ESMF_BABELTRACE_LIBS)" ; \
echo "ESMF_BABELTRACE_LIBS: $(ESMF_BABELTRACE_LIBS)" ; \
fi; \
if [ -n "$(ESMF_BABELTRACE_LIBPATH)" ] ; then \
echo "ESMF_BABELTRACE_LIBPATH: $(ESMF_BABELTRACE_LIBPATH)" ; \
echo "ESMF_BABELTRACE_LIBPATH: $(ESMF_BABELTRACE_LIBPATH)" ; \
fi; \
fi
-@if [ -n "$(ESMF_NVML)" ] ; then \
echo "ESMF_NVML: $(ESMF_NVML)" ; \
if [ -n "$(ESMF_NVML_INCLUDE)" ] ; then \
echo "ESMF_NVML_INCLUDE: $(ESMF_NVML_INCLUDE)" ; \
fi; \
if [ -n "$(ESMF_NVML_LIBS)" ] ; then \
echo "ESMF_NVML_LIBS: $(ESMF_NVML_LIBS)" ; \
fi; \
if [ -n "$(ESMF_NVML_LIBPATH)" ] ; then \
echo "ESMF_NVML_LIBPATH: $(ESMF_NVML_LIBPATH)" ; \
fi; \
fi
-@echo " "
Expand Down Expand Up @@ -644,15 +656,15 @@ endif
fi; \
fi
-@if [ -n "$(ESMF_XERCES)" ] ; then \
echo "# ESMF_XERCES: $(ESMF_XERCES)" >> $(MKINFO) ; \
echo "# ESMF_XERCES: $(ESMF_XERCES)" >> $(MKINFO) ; \
if [ -n "$(ESMF_XERCES_INCLUDE)" ] ; then \
echo "# ESMF_XERCES_INCLUDE: $(ESMF_XERCES_INCLUDE)" >> $(MKINFO) ; \
echo "# ESMF_XERCES_INCLUDE: $(ESMF_XERCES_INCLUDE)" >> $(MKINFO) ; \
fi; \
if [ -n "$(ESMF_XERCES_LIBS)" ] ; then \
echo "# ESMF_XERCES_LIBS: $(ESMF_XERCES_LIBS)" >> $(MKINFO) ; \
echo "# ESMF_XERCES_LIBS: $(ESMF_XERCES_LIBS)" >> $(MKINFO) ; \
fi; \
if [ -n "$(ESMF_XERCES_LIBPATH)" ] ; then \
echo "# ESMF_XERCES_LIBPATH: $(ESMF_XERCES_LIBPATH)" >> $(MKINFO) ; \
echo "# ESMF_XERCES_LIBPATH: $(ESMF_XERCES_LIBPATH)" >> $(MKINFO) ; \
fi; \
fi
-@if [ -n "$(ESMF_YAMLCPP)" ] ; then \
Expand All @@ -668,15 +680,39 @@ endif
fi; \
fi
-@if [ -n "$(ESMF_PROJ4)" ] ; then \
echo "# ESMF_PROJ4: $(ESMF_PROJ4)" >> $(MKINFO) ; \
echo "# ESMF_PROJ4: $(ESMF_PROJ4)" >> $(MKINFO) ; \
if [ -n "$(ESMF_PROJ4_INCLUDE)" ] ; then \
echo "# ESMF_PROJ4_INCLUDE: $(ESMF_PROJ4_INCLUDE)" >> $(MKINFO) ; \
echo "# ESMF_PROJ4_INCLUDE: $(ESMF_PROJ4_INCLUDE)" >> $(MKINFO) ; \
fi; \
if [ -n "$(ESMF_PROJ4_LIBS)" ] ; then \
echo "# ESMF_PROJ4_LIBS: $(ESMF_PROJ4_LIBS)" >> $(MKINFO) ; \
echo "# ESMF_PROJ4_LIBS: $(ESMF_PROJ4_LIBS)" >> $(MKINFO) ; \
fi; \
if [ -n "$(ESMF_PROJ4_LIBPATH)" ] ; then \
echo "# ESMF_PROJ4_LIBPATH: $(ESMF_PROJ4_LIBPATH)" >> $(MKINFO) ; \
echo "# ESMF_PROJ4_LIBPATH: $(ESMF_PROJ4_LIBPATH)" >> $(MKINFO) ; \
fi; \
fi
-@if [ -n "$(ESMF_BABELTRACE)" ] ; then \
echo "# ESMF_BABELTRACE: $(ESMF_BABELTRACE)" >> $(MKINFO) ; \
if [ -n "$(ESMF_BABELTRACE_INCLUDE)" ] ; then \
echo "# ESMF_BABELTRACE_INCLUDE:$(ESMF_BABELTRACE_INCLUDE)" >> $(MKINFO) ; \
fi; \
if [ -n "$(ESMF_BABELTRACE_LIBS)" ] ; then \
echo "# ESMF_BABELTRACE_LIBS: $(ESMF_BABELTRACE_LIBS)" >> $(MKINFO) ; \
fi; \
if [ -n "$(ESMF_BABELTRACE_LIBPATH)" ] ; then \
echo "# ESMF_BABELTRACE_LIBPATH:$(ESMF_BABELTRACE_LIBPATH)" >> $(MKINFO) ; \
fi; \
fi
-@if [ -n "$(ESMF_NVML)" ] ; then \
echo "# ESMF_NVML: $(ESMF_NVML)" >> $(MKINFO) ; \
if [ -n "$(ESMF_NVML_INCLUDE)" ] ; then \
echo "# ESMF_NVML_INCLUDE: $(ESMF_NVML_INCLUDE)" >> $(MKINFO) ; \
fi; \
if [ -n "$(ESMF_NVML_LIBS)" ] ; then \
echo "# ESMF_NVML_LIBS: $(ESMF_NVML_LIBS)" >> $(MKINFO) ; \
fi; \
if [ -n "$(ESMF_NVML_LIBPATH)" ] ; then \
echo "# ESMF_NVML_LIBPATH: $(ESMF_NVML_LIBPATH)" >> $(MKINFO) ; \
fi; \
fi
-@echo "#" >> $(MKINFO)
Expand Down
1 change: 1 addition & 0 deletions src/Infrastructure/VM/include/ESMCI_VM.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ class VM : public VMK { // inherits from ESMCI::VMK class
class VMPlan : public VMKPlan { // inherits from ESMCI::VMKPlan

public:
VMPlan(int _ndevlist=0, int *_devlist=NULL) : VMKPlan(_ndevlist,_devlist){}
int nspawn; // number of PETs this PET will spawn
VM **myvms; // pointer array of VM instances for this PET
VMK **myvmachs; // pointer array of VMK instances for this PET
Expand Down
34 changes: 27 additions & 7 deletions src/Infrastructure/VM/include/ESMCI_VMKernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,26 +306,32 @@ class VMK{
int mypet; // PET id of this instance
esmf_pthread_t mypthid; // my pthread id
// pet -> core mapping
int npets; // number of PETs in this VMK
int npets; // number of PETs in this VMK all SSI
int *lpid; // local pid (equal to rank in local MPI context)
int *pid; // pid (equal to rank in MPI_COMM_WORLD)
int *tid; // thread index
int *ncpet; // number of cores this pet references
int *nadevs; // number of accelerator devices accessible from this pet
int *nadevs;//TODO: to be removed // number of accelerator devices accessible from this pet
int **cid; // core id of the cores this pet references
int ssiCount; // number of single system images in this VMK
int ssiMinPetCount; // minimum PETs on a single system image
int ssiMaxPetCount; // maximum PETs on a single system image
int ssiLocalPetCount; // number of PETs on the same SSI as localPet (incl.)
int ssiLocalPet; // id of local PET in the local SSI
int *ssiLocalPetList; // PETs that are on the same SSI as localPet (incl.)
int devCount; // number of devices associated with this VMK all SSI
int ssiLocalDevCount;// number of devices associated with this VMK on local SSI
int *ssiLocalDevList;// list of SSI-local device indices associated with this VMK
// Use this index to make local association calls (e.g. via
// acc_set_device_num() or omp_set_default_device()), and
// to look up global device index in ssidevs array.
// general information about this VMK
int mpionly; // 0: there is multi-threading, 1: MPI-only
bool threadsflag; // threaded or none-threaded VM
// MPI Communicator handles
MPI_Comm mpi_c; // communicator across the entire VM
#if (MPI_VERSION >= 3)
MPI_Comm mpi_c_ssi; // communicator holding PETs on the same SSI
#endif
MPI_Comm mpi_c_ssi_roots; // communicator holding root PETs on each SSI
// Shared mutex and thread_finish variables. These are pointers that will be
// pointing to shared memory variables between different thread-instances of
// the VMK object.
Expand Down Expand Up @@ -355,9 +361,14 @@ class VMK{
// static info of physical machine
static int nssiid; // total number of single system image ids
static int ncores; // total number of cores in the physical machine
static int ndevs; // total number of devices in physical machine
static int ndevsSSI;// total number of devices in physical machine on SSI
//TODO: consider using SSI shared memory for the following arrays
static int *cpuid; // cpuid associated with certain core (multi-core cpus)
static int *ssiid; // single system image id to which this core belongs
static int *ssipe; // PE id on the SSI on which this PE resides
static int *ssiid; // single system image on which a certain core resides
static int *ssipe; // ssi-local PE id of a certain core
static int *ssidevs;// list of global device ids for all ssi-local devices
// begin of execution reference time
static double wtime0; // the MPI WTime at the very beginning of execution
public:
// Declaration of static data members - Definitions are in the header of
Expand Down Expand Up @@ -426,6 +437,9 @@ class VMK{
static int checkPetList(int *petList, int count);
// ensure there are no duplicate PETs listed

static int checkDevList(int *devList, int count);
// ensure there are no duplicate DEVs listed

void print() const;
void log(std::string prefix,
ESMC_LogMsgType_Flag msgType=ESMC_LOGMSG_INFO)const;
Expand Down Expand Up @@ -459,7 +473,11 @@ class VMK{
int getSsiMinPetCount() const {return ssiMinPetCount;}
int getSsiMaxPetCount() const {return ssiMaxPetCount;}
int getSsiLocalPetCount() const {return ssiLocalPetCount;}
int getSsiLocalPet() const {return ssiLocalPet;}
const int *getSsiLocalPetList() const {return ssiLocalPetList;}
int getSsiLocalDevCount() const {return ssiLocalDevCount;}
const int *getSsiLocalDevList() const {return ssiLocalDevList;}
int getDevCount() const {return devCount;}
esmf_pthread_t getLocalPthreadId() const {return mypthid;}
static bool isPthreadsEnabled(){
#ifdef ESMF_NO_PTHREADS
Expand Down Expand Up @@ -612,6 +630,8 @@ class VMKPlan{
int npets;
int nplist; // number of PETs in petlist that participate
int *petlist; // keeping sequence of parent pets
int ndevlist; // number of DEVs in devList
int *devlist; // list of associated devices
bool supportContributors; // default: false
bool eachChildPetOwnPthread; // default: false
int parentVMflag; // 0-create child VM, 1-run on parent VM
Expand All @@ -638,7 +658,7 @@ class VMKPlan{
int openmpnumthreads; // -1 default: local peCount

public:
VMKPlan();
VMKPlan(int _ndevlist=0, int *_devlist=NULL);
// native constructor (sets communication preferences to defaults)
~VMKPlan();
// native destructor
Expand Down
51 changes: 46 additions & 5 deletions src/Infrastructure/VM/interface/ESMCI_VM_F.C
Original file line number Diff line number Diff line change
Expand Up @@ -566,10 +566,11 @@ extern "C" {
// return successfully
if (rc!=NULL) *rc = ESMF_SUCCESS;
}
void FTN_X(c_esmc_vmget)(ESMCI::VM **vm, int *localPet, int *currentSsiPe,

void FTN_X(c_esmc_vmget)(ESMCI::VM **vm, int *localPet, int *currentSsiPe,
int *petCount, int *peCount, int *ssiCount, int *ssiMinPetCount,
int *ssiMaxPetCount, int *ssiLocalPetCount, int *mpiCommunicator,
int *ssiMaxPetCount, int *ssiLocalPetCount, int *ssiLocalPet,
int *ssiLocalDevCount, int *mpiCommunicator,
ESMC_Logical *pthreadsEnabledFlag, ESMC_Logical *openMPEnabledFlag,
ESMC_Logical *ssiSharedMemoryEnabledFlag, int *rc){
#undef ESMC_METHOD
Expand Down Expand Up @@ -601,6 +602,10 @@ extern "C" {
*ssiMaxPetCount = (*vm)->getSsiMaxPetCount();
if (ESMC_NOT_PRESENT_FILTER(ssiLocalPetCount) != ESMC_NULL_POINTER)
*ssiLocalPetCount = (*vm)->getSsiLocalPetCount();
if (ESMC_NOT_PRESENT_FILTER(ssiLocalPet) != ESMC_NULL_POINTER)
*ssiLocalPet = (*vm)->getSsiLocalPet();
if (ESMC_NOT_PRESENT_FILTER(ssiLocalDevCount) != ESMC_NULL_POINTER)
*ssiLocalDevCount = (*vm)->getSsiLocalDevCount();
if (ESMC_NOT_PRESENT_FILTER(mpiCommunicator) != ESMC_NULL_POINTER){
mpiCommTemp = (*vm)->getMpi_c();
#ifdef ESMF_DONT_HAVE_MPI_COMM_C2F
Expand Down Expand Up @@ -632,6 +637,36 @@ extern "C" {
if (rc!=NULL) *rc = ESMF_SUCCESS;
}

void FTN_X(c_esmc_vmgetssilocaldevlist)(ESMCI::VM **vm,
ESMCI::InterArray<int> *ssiLocalDevListArg, int *rc){
#undef ESMC_METHOD
#define ESMC_METHOD "c_esmc_vmgetssilocaldevlist()"
// Initialize return code; assume routine not implemented
if (rc!=NULL) *rc = ESMC_RC_NOT_IMPL;
// test for NULL pointer via macro before calling any class methods
ESMCI_NULL_CHECK_PRC(vm, rc)
ESMCI_NULL_CHECK_PRC(*vm, rc)
// access and transfer information
if (ssiLocalDevListArg->dimCount != 1){
ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_INCOMP,
"ssiLocalDevListArg must enter with one dimension",
ESMC_CONTEXT, rc);
return;
}
int ssiLocalDevCount = ssiLocalDevListArg->extent[0];
if (ssiLocalDevCount != (*vm)->getSsiLocalDevCount()){
ESMC_LogDefault.MsgFoundError(ESMC_RC_ARG_INCOMP,
"ssiLocalDevListArg provide ssiLocalDevCount many elements",
ESMC_CONTEXT, rc);
return;
}
int *ssiLocalDevList = ssiLocalDevListArg->array;
memcpy(ssiLocalDevList, (*vm)->getSsiLocalDevList(),
ssiLocalDevCount * sizeof(int));
// return successfully
if (rc!=NULL) *rc = ESMF_SUCCESS;
}

void FTN_X(c_esmc_vmgetmpicommnull)(int *mpiCommunicator, int *rc){
#undef ESMC_METHOD
#define ESMC_METHOD "c_esmc_vmgetmpicommnull()"
Expand Down Expand Up @@ -1272,7 +1307,8 @@ extern "C" {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void FTN_X(c_esmc_vmplanconstruct)(ESMCI::VMPlan **ptr, ESMCI::VM **vm,
int *npetlist, int *petlist, ESMC_ContextFlag *contextflag, int *rc){
int *npetlist, int *petlist, int *ndevlist, int *devlist,
ESMC_ContextFlag *contextflag, int *rc){
#undef ESMC_METHOD
#define ESMC_METHOD "c_esmc_vmplanconstruct()"
// Initialize return code; assume routine not implemented
Expand All @@ -1286,7 +1322,12 @@ extern "C" {
if (ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU,
ESMC_CONTEXT, rc)) return;
}
(*ptr) = new ESMCI::VMPlan;
if (*ndevlist > 0){
int localrc = ESMCI::VMK::checkDevList(devlist, *ndevlist);
if (ESMC_LogDefault.MsgFoundError(localrc, ESMCI_ERR_PASSTHRU,
ESMC_CONTEXT, rc)) return;
}
(*ptr) = new ESMCI::VMPlan(*ndevlist, devlist);
if (*contextflag==ESMF_CHILD_IN_PARENT_VM)
(*ptr)->vmkplan_useparentvm(**vm);
else if (*npetlist > 0){
Expand Down
Loading

0 comments on commit 3a1ad6f

Please sign in to comment.